diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-10 21:51:57 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-10 21:51:57 +0300 |
| commit | 425c86179791cd32e2d4ce0ae7f269940b4b0663 (patch) | |
| tree | e52c4383a6d69216cbe2a32638bf90841d7e2189 /vendor/libghostty-vt/src/font | |
| parent | 90e947a6e26e02f10ec7f1694c4eb5b78a86406b (diff) | |
chore: upgrade libghostty and preserve terminal behavior (#3906)
Diffstat (limited to 'vendor/libghostty-vt/src/font')
63 files changed, 595 insertions, 391 deletions
diff --git a/vendor/libghostty-vt/src/font/Atlas.zig b/vendor/libghostty-vt/src/font/Atlas.zig index d1206457..66e91c8f 100644 --- a/vendor/libghostty-vt/src/font/Atlas.zig +++ b/vendor/libghostty-vt/src/font/Atlas.zig @@ -32,7 +32,7 @@ data: []u8, size: u32 = 0, /// The nodes (rectangles) of available space. -nodes: std.ArrayListUnmanaged(Node) = .{}, +nodes: std.ArrayList(Node) = .empty, /// The format of the texture data being written into the Atlas. This must be /// uniform for all textures in the Atlas. If you have some textures with @@ -104,7 +104,7 @@ pub fn init(alloc: Allocator, size: u32, format: Format) Allocator.Error!Atlas { var result = Atlas{ .data = try alloc.alloc(u8, size * size * format.depth()), .size = size, - .nodes = .{}, + .nodes = .empty, .format = format, }; errdefer result.deinit(alloc); diff --git a/vendor/libghostty-vt/src/font/Collection.zig b/vendor/libghostty-vt/src/font/Collection.zig index 5d7bfa51..6c646d9f 100644 --- a/vendor/libghostty-vt/src/font/Collection.zig +++ b/vendor/libghostty-vt/src/font/Collection.zig @@ -28,6 +28,7 @@ const Library = font.Library; const Metrics = font.Metrics; const Presentation = font.Presentation; const Style = font.Style; +const SegmentedList = @import("../datastruct/segmented_list.zig").SegmentedList; const log = std.log.scoped(.font_collection); @@ -662,7 +663,7 @@ fn scaleFactor( return primary_metric / face_metric; } -const UpdateMetricsError = error{ +pub const UpdateMetricsError = error{ CannotLoadPrimaryFont, }; @@ -694,7 +695,7 @@ pub fn updateMetrics(self: *Collection) UpdateMetricsError!void { /// /// WARNING: We cannot use any prealloc yet for the segmented list because /// the collection is copied around by value and pointers aren't stable. -const StyleArray = std.EnumArray(Style, std.SegmentedList(EntryOrAlias, 0)); +const StyleArray = std.EnumArray(Style, SegmentedList(EntryOrAlias, 0)); /// Load options are used to configure all the details a Collection /// needs to load deferred faces. @@ -894,7 +895,7 @@ pub const Index = packed struct(Index.Backing) { /// The number of bits we use for the index. const idx_bits = backing_bits - @typeInfo(@typeInfo(Style).@"enum".tag_type).int.bits; - pub const IndexInt = @Type(.{ .int = .{ .signedness = .unsigned, .bits = idx_bits } }); + pub const IndexInt = @Int(.unsigned, idx_bits); /// The special-case fonts that we support. pub const Special = enum(IndexInt) { diff --git a/vendor/libghostty-vt/src/font/DeferredFace.zig b/vendor/libghostty-vt/src/font/DeferredFace.zig index 425f3c28..9a8124c9 100644 --- a/vendor/libghostty-vt/src/font/DeferredFace.zig +++ b/vendor/libghostty-vt/src/font/DeferredFace.zig @@ -184,7 +184,8 @@ pub fn name(self: DeferredFace, buf: []u8) ![]const u8 { .coretext_harfbuzz, .coretext_noshape, => if (self.ct) |ct| { - const display_name = ct.font.copyDisplayName(); + const display_name = ct.font.copyDisplayName() orelse + return self.familyName(buf); return display_name.cstringPtr(.utf8) orelse unsupported: { // "NULL if the internal storage of theString does not allow // this to be returned efficiently." In this case, we need diff --git a/vendor/libghostty-vt/src/font/Metrics.zig b/vendor/libghostty-vt/src/font/Metrics.zig index 60a300b0..36cfe009 100644 --- a/vendor/libghostty-vt/src/font/Metrics.zig +++ b/vendor/libghostty-vt/src/font/Metrics.zig @@ -589,23 +589,21 @@ pub const Modifier = union(enum) { /// Key is an enum of all the available metrics keys. pub const Key = key: { const field_infos = std.meta.fields(Metrics); - var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined; + var names: [field_infos.len][]const u8 = undefined; + var raw_values: [field_infos.len]comptime_int = undefined; var count: usize = 0; - for (field_infos, 0..) |field, i| { + for (field_infos, &names, &raw_values, 0..) |field, *name, *raw, i| { if (field.type != u32 and field.type != i32 and field.type != f64) continue; - enumFields[i] = .{ .name = field.name, .value = i }; + name.* = field.name; + raw.* = i; count += 1; } - var decls = [_]std.builtin.Type.Declaration{}; - break :key @Type(.{ - .@"enum" = .{ - .tag_type = std.math.IntFittingRange(0, count - 1), - .fields = enumFields[0..count], - .decls = &decls, - .is_exhaustive = true, - }, - }); + const TagInt = std.math.IntFittingRange(0, count - 1); + var values: [count]TagInt = undefined; + for (raw_values, &values) |raw, *v| v.* = raw; + + break :key @Enum(TagInt, .exhaustive, names[0..count], &values); }; // NOTE: This is purposely not pub because we want to force outside callers diff --git a/vendor/libghostty-vt/src/font/SharedGrid.zig b/vendor/libghostty-vt/src/font/SharedGrid.zig index 18870aab..92cf0500 100644 --- a/vendor/libghostty-vt/src/font/SharedGrid.zig +++ b/vendor/libghostty-vt/src/font/SharedGrid.zig @@ -34,6 +34,7 @@ const Metrics = font.Metrics; const Presentation = font.Presentation; const Style = font.Style; const RenderOptions = font.Glyph.RenderOptions; +const global = @import("../global.zig"); const log = std.log.scoped(.font_shared_grid); @@ -60,7 +61,7 @@ metrics: Metrics, /// this directly if they need to i.e. access the atlas directly. Because /// callers can use this lock directly, maintainers need to be extra careful /// to review call sites to ensure they are using the lock correctly. -lock: std.Thread.RwLock, +lock: std.Io.RwLock, pub const init_tw = tripwire.module(enum { codepoints_capacity, @@ -68,6 +69,8 @@ pub const init_tw = tripwire.module(enum { reload_metrics, }, init); +pub const InitError = std.mem.Allocator.Error || Collection.UpdateMetricsError; + /// Initialize the grid. /// /// The resolver must have a collection that supports deferred loading @@ -80,7 +83,7 @@ pub const init_tw = tripwire.module(enum { pub fn init( alloc: Allocator, resolver: CodepointResolver, -) !SharedGrid { +) InitError!SharedGrid { const tw = init_tw; // We need to support loading options since we use the size data @@ -95,7 +98,7 @@ pub fn init( .resolver = resolver, .atlas_grayscale = atlas_grayscale, .atlas_color = atlas_color, - .lock = .{}, + .lock = .init, .metrics = undefined, // Loaded below }; @@ -162,14 +165,14 @@ pub fn getIndex( // Fast path: the cache has the value. This is almost always true and // only requires a read lock. { - self.lock.lockShared(); - defer self.lock.unlockShared(); + self.lock.lockSharedUncancelable(global.io()); + defer self.lock.unlockShared(global.io()); if (self.codepoints.get(key)) |v| return v; } // Slow path: we need to search this codepoint - self.lock.lock(); - defer self.lock.unlock(); + self.lock.lockUncancelable(global.io()); + defer self.lock.unlock(global.io()); // Try to get it, if it is now in the cache another thread beat us to it. const gop = try self.codepoints.getOrPut(alloc, key); @@ -201,8 +204,8 @@ pub fn hasCodepoint( cp: u32, p: ?Presentation, ) bool { - self.lock.lockShared(); - defer self.lock.unlockShared(); + self.lock.lockSharedUncancelable(global.io()); + defer self.lock.unlockShared(global.io()); return self.resolver.collection.hasCodepoint( idx, cp, @@ -236,8 +239,8 @@ pub fn renderCodepoint( // Get the glyph for the font const glyph_index = glyph_index: { - self.lock.lockShared(); - defer self.lock.unlockShared(); + self.lock.lockSharedUncancelable(global.io()); + defer self.lock.unlockShared(global.io()); const face = try self.resolver.collection.getFace(index); break :glyph_index face.glyphIndex(cp) orelse return null; }; @@ -250,6 +253,127 @@ pub const renderGlyph_tw = tripwire.module(enum { get_presentation, }, renderGlyph); +// FIXME: This gets a massive manual error set for now because there is an +// equally massive call stack in the get_presentation tracepoint. This should +// be consolidated into something better ASAP. +pub const RenderGlyphError = error{ + ArrayTooLarge, + AtlasFull, + BadArgument, + BbxTooBig, + BitmapHandlingError, + CMapTableMissing, + CannotOpenResource, + CannotOpenStream, + CannotRenderGlyph, + CodeOverflow, + CorruptedFontGlyphs, + CorruptedFontHeader, + CouldNotFindContext, + DebugOpCode, + DeferredLoadingUnavailable, + DivideByZero, + ENDFInExecStream, + ExecutionTooLong, + FontHasNoFile, + FontPathCantDecode, + FontconfigFailed, + FontconfigNoId, + FontconfigNoMatch, + FontconfigTypeMismatch, + GlyphResizeFailed, + HarfbuzzFailed, + HmtxTableMissing, + HorizHeaderMissing, + Ignore, + IndexOutOfBounds, + InvalidArgument, + InvalidAtlasFormat, + InvalidCacheHandle, + InvalidCharMapFormat, + InvalidCharMapHandle, + InvalidCharacterCode, + InvalidCodeRange, + InvalidComposite, + InvalidDriverHandle, + InvalidFaceHandle, + InvalidFileFormat, + InvalidFrameOperation, + InvalidFrameRead, + InvalidGlyphFormat, + InvalidGlyphIndex, + InvalidHandle, + InvalidHeight, + InvalidHorizMetrics, + InvalidLibraryHandle, + InvalidMatrix, + InvalidOffset, + InvalidOpcode, + InvalidOutline, + InvalidPPem, + InvalidPixelSize, + InvalidPostTable, + InvalidPostTableFormat, + InvalidReference, + InvalidSVGTable, + InvalidSizeHandle, + InvalidSlotHandle, + InvalidState, + InvalidStreamHandle, + InvalidStreamOperation, + InvalidStreamRead, + InvalidStreamSeek, + InvalidStreamSkip, + InvalidTable, + InvalidVersion, + InvalidVertMetrics, + InvalidWidth, + LocationsMissing, + LowerModuleVersion, + MathError, + MissingBbxField, + MissingCharsField, + MissingEncodingField, + MissingFontField, + MissingFontboundingboxField, + MissingModule, + MissingProperty, + MissingSizeField, + MissingStartcharField, + MissingStartfontField, + NameTableMissing, + NestedDEFS, + NestedFrameAccess, + NoCurrentPoint, + NoUnicodeGlyphName, + OutOfMemory, + PathNotClosed, + PixelSourceNotPreMultiplied, + PostTableMissing, + RasterCorrupted, + RasterNegativeHeight, + RasterOverflow, + RasterUninitialized, + SpecialHasNoFace, + StackOverflow, + StackUnderflow, + Syntax, + TableMissing, + TooFewArguments, + TooManyCaches, + TooManyDrivers, + TooManyExtensions, + TooManyFunctionDefs, + TooManyHints, + TooManyInstructionDefs, + UnimplementedFeature, + UnknownFileFormat, + UnknownFreetypeError, + UnlistedObject, + UnsupportedGlyphFormat, + WrongAtlas, +}; + /// Render a glyph index. This automatically determines the correct texture /// atlas to use and caches the result. pub fn renderGlyph( @@ -258,7 +382,7 @@ pub fn renderGlyph( index: Collection.Index, glyph_index: u32, opts: RenderOptions, -) !Render { +) RenderGlyphError!Render { const tw = renderGlyph_tw; const key: GlyphKey = .{ .index = index, .glyph = glyph_index, .opts = opts }; @@ -266,14 +390,14 @@ pub fn renderGlyph( // Fast path: the cache has the value. This is almost always true and // only requires a read lock. { - self.lock.lockShared(); - defer self.lock.unlockShared(); + self.lock.lockSharedUncancelable(global.io()); + defer self.lock.unlockShared(global.io()); if (self.glyphs.get(key)) |v| return v; } // Slow path: we need to search this codepoint - self.lock.lock(); - defer self.lock.unlock(); + self.lock.lockUncancelable(global.io()); + defer self.lock.unlock(global.io()); const gop = try self.glyphs.getOrPut(alloc, key); if (gop.found_existing) return gop.value_ptr.*; @@ -468,8 +592,8 @@ test "renderGlyph error after cache insert rolls back cache entry" { // Get the glyph index for 'A' const glyph_index = glyph_index: { - grid.lock.lockShared(); - defer grid.lock.unlockShared(); + grid.lock.lockSharedUncancelable(testing.io); + defer grid.lock.unlockShared(testing.io); const face = try grid.resolver.collection.getFace(idx); break :glyph_index face.glyphIndex('A').?; }; diff --git a/vendor/libghostty-vt/src/font/SharedGridSet.zig b/vendor/libghostty-vt/src/font/SharedGridSet.zig index 9d8148bd..dc7d8dd7 100644 --- a/vendor/libghostty-vt/src/font/SharedGridSet.zig +++ b/vendor/libghostty-vt/src/font/SharedGridSet.zig @@ -28,6 +28,7 @@ const SharedGrid = font.SharedGrid; const discovery = @import("discovery.zig"); const configpkg = @import("../config.zig"); const Config = configpkg.Config; +const global = @import("../global.zig"); const log = std.log.scoped(.font_shared_grid_set); @@ -44,7 +45,7 @@ font_lib: Library, font_discover: ?Discover = null, /// Lock to protect multi-threaded access to the map. -lock: std.Thread.Mutex = .{}, +lock: std.Io.Mutex = .init, pub const InitError = Library.InitError; @@ -79,8 +80,8 @@ pub fn deinit(self: *SharedGridSet) void { /// Returns the number of cached grids. pub fn count(self: *SharedGridSet) usize { - self.lock.lock(); - defer self.lock.unlock(); + self.lock.lockUncancelable(global.io()); + defer self.lock.unlock(global.io()); return self.map.count(); } @@ -100,8 +101,8 @@ pub fn ref( var key = try Key.init(self.alloc, config, font_size); errdefer key.deinit(); - self.lock.lock(); - defer self.lock.unlock(); + self.lock.lockUncancelable(global.io()); + defer self.lock.unlock(global.io()); const gop = try self.map.getOrPut(self.alloc, key); if (gop.found_existing) { @@ -339,6 +340,24 @@ fn collection( // specifying a font-family for emoji. if (comptime builtin.target.os.tag.isDarwin() and Discover != void) apple_emoji: { const disco = try self.discover() orelse break :apple_emoji; + + // Fast path: we know the exact name of the font we want so we + // can look it up directly, which is sometimes significantly faster than + // full discovery (e.g. CoreText). + if (@hasDecl(Discover, "discoverExactFamily")) { + if (try disco.discoverExactFamily( + "Apple Color Emoji", + )) |face| { + _ = try c.addDeferred(self.alloc, face, .{ + .style = .regular, + .fallback = true, + // No size adjustment for emojis. + .size_adjustment = .none, + }); + break :apple_emoji; + } + } + var disco_it = try disco.discover(self.alloc, .{ .family = "Apple Color Emoji", }); @@ -392,8 +411,8 @@ fn collection( /// Decrement the ref count for the given key. If the ref count is zero, /// the grid will be deinitialized and removed from the map.j:w pub fn deref(self: *SharedGridSet, key: Key) void { - self.lock.lock(); - defer self.lock.unlock(); + self.lock.lockUncancelable(global.io()); + defer self.lock.unlock(global.io()); const entry = self.map.getEntry(key) orelse return; assert(entry.value_ptr.ref >= 1); diff --git a/vendor/libghostty-vt/src/font/discovery.zig b/vendor/libghostty-vt/src/font/discovery.zig index b945aa01..1847693a 100644 --- a/vendor/libghostty-vt/src/font/discovery.zig +++ b/vendor/libghostty-vt/src/font/discovery.zig @@ -12,6 +12,7 @@ const Face = @import("main.zig").Face; const Library = @import("main.zig").Library; const Presentation = @import("main.zig").Presentation; const Variation = @import("main.zig").face.Variation; +const global = @import("../global.zig"); const log = std.log.scoped(.discovery); @@ -349,6 +350,22 @@ pub const CoreText = struct { _ = self; } + /// Warm up the system font registry. + /// + /// The first CoreText query in a process initializes the system font + /// database, which takes multiple milliseconds, while subsequent + /// queries are microseconds. + pub fn warmup() void { + const name = macos.foundation.String.createWithBytes( + "AppleColorEmoji", + .utf8, + false, + ) catch return; + defer name.release(); + const ct_font = macos.text.Font.createWithName(name, 12) catch return; + ct_font.release(); + } + /// Discover fonts from a descriptor. This returns an iterator that can /// be used to build up the deferred fonts. pub fn discover(self: *const CoreText, alloc: Allocator, desc: Descriptor) !DiscoverIterator { @@ -382,6 +399,54 @@ pub const CoreText = struct { }; } + /// Discover a font by its exact name (family, full, or PostScript + /// name). This is significantly faster than `discover` because it + /// avoids the system-wide font matching that CTFontCollection does + /// (which takes multiple milliseconds). This should be preferred + /// when the desired font is known exactly, e.g. system fonts such + /// as Apple Color Emoji. + /// + /// Returns null if no font with this exact family name exists; + /// CoreText fallback fonts are never returned. + pub fn discoverExactFamily( + self: *const CoreText, + family: []const u8, + ) !?DeferredFace { + _ = self; + + const family_str = try macos.foundation.String.createWithBytes( + family, + .utf8, + false, + ); + defer family_str.release(); + + // Create our font. We need a size to initialize it so we use size + // 12 but we will alter the size later (same as DiscoverIterator). + const ct_font = try macos.text.Font.createWithName(family_str, 12); + + // CTFontCreateWithName never returns null: if the requested font + // isn't installed it returns a substitute font. Verify we got + // the family we asked for, otherwise report not found. + const found: bool = found: { + const actual = ct_font.copyFamilyName(); + defer actual.release(); + var buf: [256]u8 = undefined; + const actual_slice = actual.cstring(&buf, .utf8) orelse + break :found false; + break :found std.mem.eql(u8, actual_slice, family); + }; + if (!found) { + ct_font.release(); + return null; + } + + return .{ .ct = .{ + .font = ct_font, + .variations = &.{}, + } }; + } + pub fn discoverFallback( self: *const CoreText, alloc: Allocator, @@ -942,15 +1007,15 @@ pub const Windows = struct { desc: Descriptor, variations: []const Variation, state: State, - dir: ?std.fs.Dir, - iter: ?std.fs.Dir.Iterator, + dir: ?std.Io.Dir, + iter: ?std.Io.Dir.Iterator, system_path: ?[:0]const u8, user_path: ?[:0]const u8, const State = enum { system, user, done }; pub fn deinit(self: *DiscoverIterator) void { - if (self.dir) |*d| d.close(); + if (self.dir) |*d| d.close(global.io()); if (self.system_path) |p| self.alloc.free(p); if (self.user_path) |p| self.alloc.free(p); self.* = undefined; @@ -967,7 +1032,8 @@ pub const Windows = struct { continue; }; self.system_path = path; - self.dir = std.fs.openDirAbsoluteZ( + self.dir = std.Io.Dir.openDirAbsolute( + global.io(), path, .{ .iterate = true }, ) catch { @@ -982,7 +1048,8 @@ pub const Windows = struct { continue; }; self.user_path = path; - self.dir = std.fs.openDirAbsoluteZ( + self.dir = std.Io.Dir.openDirAbsolute( + global.io(), path, .{ .iterate = true }, ) catch { @@ -995,9 +1062,9 @@ pub const Windows = struct { } } - const entry = (self.iter.?.next() catch null) orelse { + const entry = (self.iter.?.next(global.io()) catch null) orelse { // Finished this directory; advance state. - if (self.dir) |*d| d.close(); + if (self.dir) |*d| d.close(global.io()); self.dir = null; self.iter = null; self.state = switch (self.state) { @@ -1020,7 +1087,7 @@ pub const Windows = struct { /// Windows install but we just skip the directory rather than /// falling back to a hardcoded drive letter. fn systemFontsPath(self: *DiscoverIterator) ?[:0]const u8 { - const systemroot = std.process.getEnvVarOwned( + const systemroot = global.environ().getAlloc( self.alloc, "SYSTEMROOT", ) catch return null; @@ -1034,7 +1101,7 @@ pub const Windows = struct { } fn userFontsPath(self: *DiscoverIterator) ?[:0]const u8 { - const local_appdata = std.process.getEnvVarOwned( + const local_appdata = global.environ().getAlloc( self.alloc, "LOCALAPPDATA", ) catch return null; diff --git a/vendor/libghostty-vt/src/font/face/coretext.zig b/vendor/libghostty-vt/src/font/face/coretext.zig index cd8bc7d8..4b2af153 100644 --- a/vendor/libghostty-vt/src/font/face/coretext.zig +++ b/vendor/libghostty-vt/src/font/face/coretext.zig @@ -926,6 +926,7 @@ const ColorState = struct { const svg = opentype.SVG.init(ptr[0..len]) catch |err| { return switch (err) { error.EndOfStream, + error.ReadFailed, error.SVGVersionNotSupported, => error.InvalidSVGTable, }; diff --git a/vendor/libghostty-vt/src/font/face/freetype.zig b/vendor/libghostty-vt/src/font/face/freetype.zig index d04fe180..14a378e5 100644 --- a/vendor/libghostty-vt/src/font/face/freetype.zig +++ b/vendor/libghostty-vt/src/font/face/freetype.zig @@ -18,6 +18,7 @@ const Library = font.Library; const opentype = @import("../opentype.zig"); const quirks = @import("../../quirks.zig"); const config = @import("../../config.zig"); +const global = @import("../../global.zig"); const F26Dot6 = opentype.sfnt.F26Dot6; @@ -41,7 +42,7 @@ pub const Face = struct { /// /// This means that anywhere where `self.face.loadGlyph` /// is called, this mutex must be held. - ft_mutex: *std.Thread.Mutex, + ft_mutex: *std.Io.Mutex, /// Harfbuzz font corresponding to this face. hb_font: harfbuzz.Font, @@ -68,8 +69,8 @@ pub const Face = struct { index: i32, opts: font.face.Options, ) !Face { - lib.mutex.lock(); - defer lib.mutex.unlock(); + lib.mutex.lockUncancelable(global.io()); + defer lib.mutex.unlock(global.io()); const face = try lib.lib.initFace(path, index); errdefer face.deinit(); return try initFace(lib, face, opts); @@ -81,8 +82,8 @@ pub const Face = struct { source: [:0]const u8, opts: font.face.Options, ) !Face { - lib.mutex.lock(); - defer lib.mutex.unlock(); + lib.mutex.lockUncancelable(global.io()); + defer lib.mutex.unlock(global.io()); const face = try lib.lib.initMemoryFace(source, 0); errdefer face.deinit(); return try initFace(lib, face, opts); @@ -99,9 +100,9 @@ pub const Face = struct { var hb_font = try harfbuzz.freetype.createFont(face.handle); errdefer hb_font.destroy(); - const ft_mutex = try lib.alloc.create(std.Thread.Mutex); + const ft_mutex = try lib.alloc.create(std.Io.Mutex); errdefer lib.alloc.destroy(ft_mutex); - ft_mutex.* = .{}; + ft_mutex.* = .init; var result: Face = .{ .lib = lib, @@ -142,8 +143,8 @@ pub const Face = struct { pub fn deinit(self: *Face) void { self.lib.alloc.destroy(self.ft_mutex); { - self.lib.mutex.lock(); - defer self.lib.mutex.unlock(); + self.lib.mutex.lockUncancelable(global.io()); + defer self.lib.mutex.unlock(global.io()); self.face.deinit(); } @@ -330,8 +331,8 @@ pub const Face = struct { /// Returns true if the given glyph ID is colorized. pub fn isColorGlyph(self: *const Face, glyph_id: u32) bool { - self.ft_mutex.lock(); - defer self.ft_mutex.unlock(); + self.ft_mutex.lockUncancelable(global.io()); + defer self.ft_mutex.unlock(global.io()); // Load the glyph and see what pixel mode it renders with. // All modes other than BGRA are non-color. @@ -429,8 +430,8 @@ pub const Face = struct { glyph_index: u32, opts: font.Glyph.RenderOptions, ) !Glyph { - self.ft_mutex.lock(); - defer self.ft_mutex.unlock(); + self.ft_mutex.lockUncancelable(global.io()); + defer self.ft_mutex.unlock(global.io()); // Load the glyph. try self.face.loadGlyph(glyph_index, self.glyphLoadFlags(opts.constraint.doesAnything())); @@ -541,7 +542,7 @@ pub const Face = struct { else 0.0; - const outline = &glyph.*.outline; + const outline: *freetype.c.FT_Outline = &glyph.*.outline; for (outline.points[0..@intCast(outline.n_points)]) |*p| { // Convert to f64 for processing var px = f26dot6ToF64(p.x); @@ -971,8 +972,8 @@ pub const Face = struct { // the metrics provided by FreeType, and set ascii_height to null as // it's optional. const cell_width: f64, const ascii_height: ?f64 = measurements: { - self.ft_mutex.lock(); - defer self.ft_mutex.unlock(); + self.ft_mutex.lockUncancelable(global.io()); + defer self.ft_mutex.unlock(global.io()); var max: f64 = 0.0; var top: f64 = 0.0; @@ -1024,8 +1025,8 @@ pub const Face = struct { break :heights .{ cap: { - self.ft_mutex.lock(); - defer self.ft_mutex.unlock(); + self.ft_mutex.lockUncancelable(global.io()); + defer self.ft_mutex.unlock(global.io()); if (face.getCharIndex('H')) |glyph_index| { if (face.loadGlyph(glyph_index, self.glyphLoadFlags(false))) { break :cap getGlyphSize(face.handle.*.glyph).height; @@ -1034,8 +1035,8 @@ pub const Face = struct { break :cap null; }, ex: { - self.ft_mutex.lock(); - defer self.ft_mutex.unlock(); + self.ft_mutex.lockUncancelable(global.io()); + defer self.ft_mutex.unlock(global.io()); if (face.getCharIndex('x')) |glyph_index| { if (face.loadGlyph(glyph_index, self.glyphLoadFlags(false))) { break :ex getGlyphSize(face.handle.*.glyph).height; @@ -1048,8 +1049,8 @@ pub const Face = struct { // Measure "水" (CJK water ideograph, U+6C34) for our ic width. const ic_width: ?f64 = ic_width: { - self.ft_mutex.lock(); - defer self.ft_mutex.unlock(); + self.ft_mutex.lockUncancelable(global.io()); + defer self.ft_mutex.unlock(global.io()); const glyph = face.getCharIndex('水') orelse break :ic_width null; diff --git a/vendor/libghostty-vt/src/font/glyf_rasterize.zig b/vendor/libghostty-vt/src/font/glyf_rasterize.zig index f321214b..f288ff0a 100644 --- a/vendor/libghostty-vt/src/font/glyf_rasterize.zig +++ b/vendor/libghostty-vt/src/font/glyf_rasterize.zig @@ -99,14 +99,16 @@ pub fn rasterize( // bitmap just like an empty outline. if (bounds.width() == 0 or bounds.height() == 0) return Bitmap.initEmpty(alloc, width, height); - // Build the surface we'll draw on. This is a simple alpha8 drawing. - var sfc: z2d.Surface = try .init( + // Build the surface directly over the bitmap we'll return. + const data = try alloc.alloc(u8, @as(usize, width) * @as(usize, height)); + errdefer alloc.free(data); + var sfc: z2d.Surface = .initBuffer( .image_surface_alpha8, - alloc, + null, + std.mem.bytesAsSlice(z2d.pixel.Alpha8, data), @intCast(width), @intCast(height), ); - defer sfc.deinit(alloc); var path: z2d.Path = .empty; defer path.deinit(alloc); @@ -133,7 +135,7 @@ pub fn rasterize( return .{ .width = width, .height = height, - .data = try alloc.dupe(u8, std.mem.sliceAsBytes(sfc.image_surface_alpha8.buf)), + .data = data, }; } diff --git a/vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig b/vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig index e8ad70fd..881c0ed9 100644 --- a/vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig +++ b/vendor/libghostty-vt/src/font/glyf_rasterize_png_test.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const wuffs = @import("wuffs"); const z2d = @import("z2d"); +const compat_file = @import("../lib/compat/file.zig"); const glyf_rasterize = @import("glyf_rasterize.zig"); const glyf = @import("opentype/glyf.zig"); @@ -107,37 +108,38 @@ fn drawCellBoxes(atlas: *z2d.Surface, y: usize, cell_width: usize, cell_height: /// `glyf_rasterize_diff.png`, where red is reference-only coverage and green is /// newly generated coverage. Returns true when a difference was found. fn diffAtlas( + io: std.Io, alloc: Allocator, atlas: *z2d.Surface, generated_path: []const u8, ) !bool { const ref_path = "src/font/testdata/glyf_rasterize.png"; - const generated_file = try std.fs.openFileAbsolute(generated_path, .{ .mode = .read_only }); - defer generated_file.close(); - const generated_bytes = try generated_file.readToEndAlloc(alloc, std.math.maxInt(usize)); + const generated_file = try std.Io.Dir.openFileAbsolute(io, generated_path, .{ .mode = .read_only }); + defer generated_file.close(io); + const generated_bytes = try compat_file.readToEndAlloc(generated_file, alloc, std.math.maxInt(usize)); defer alloc.free(generated_bytes); - const cwd_absolute = try std.fs.cwd().realpathAlloc(alloc, "."); + const cwd_absolute = try std.Io.Dir.cwd().realPathFileAlloc(io, ".", alloc); defer alloc.free(cwd_absolute); - const ref_file = std.fs.cwd().openFile(ref_path, .{ .mode = .read_only }) catch |err| { + const ref_file = std.Io.Dir.cwd().openFile(io, ref_path, .{ .mode = .read_only }) catch |err| { log.err("Can't open reference file {s}: {}", .{ ref_path, err }); const test_path = try std.fmt.allocPrint(alloc, "{s}/glyf_rasterize_test.png", .{cwd_absolute}); defer alloc.free(test_path); - try std.fs.copyFileAbsolute(generated_path, test_path, .{}); + try std.Io.Dir.copyFileAbsolute(generated_path, test_path, io, .{}); return true; }; - defer ref_file.close(); - const ref_bytes = try ref_file.readToEndAlloc(alloc, std.math.maxInt(usize)); + defer ref_file.close(io); + const ref_bytes = try compat_file.readToEndAlloc(ref_file, alloc, std.math.maxInt(usize)); defer alloc.free(ref_bytes); if (std.mem.eql(u8, generated_bytes, ref_bytes)) return false; const test_path = try std.fmt.allocPrint(alloc, "{s}/glyf_rasterize_test.png", .{cwd_absolute}); defer alloc.free(test_path); - try std.fs.copyFileAbsolute(generated_path, test_path, .{}); + try std.Io.Dir.copyFileAbsolute(generated_path, test_path, io, .{}); const ref_rgba = try wuffs.png.decode(alloc, ref_bytes); defer alloc.free(ref_rgba.data); @@ -185,7 +187,7 @@ fn diffAtlas( } const diff_path = "./glyf_rasterize_diff.png"; - try z2d.png_exporter.writeToPNGFile(diff, diff_path, .{}); + try z2d.png_exporter.writeToPNGFile(io, diff, diff_path, .{}); log.err( "glyf rasterize visual output differs from reference: test={s}, reference={s}, diff={s}", .{ test_path, ref_path, diff_path }, @@ -290,12 +292,12 @@ test "glyf_rasterize: bubbletea glyph protocol examples match reference image" { var dir = testing.tmpDir(.{}); defer dir.cleanup(); - const tmp_dir = try dir.dir.realpathAlloc(alloc, "."); + const tmp_dir = try dir.dir.realPathFileAlloc(testing.io, ".", alloc); defer alloc.free(tmp_dir); const generated_path = try std.fmt.allocPrint(alloc, "{s}/glyf_rasterize.png", .{tmp_dir}); defer alloc.free(generated_path); - try z2d.png_exporter.writeToPNGFile(atlas, generated_path, .{}); + try z2d.png_exporter.writeToPNGFile(testing.io, atlas, generated_path, .{}); - try testing.expect(!try diffAtlas(alloc, &atlas, generated_path)); + try testing.expect(!try diffAtlas(testing.io, alloc, &atlas, generated_path)); } diff --git a/vendor/libghostty-vt/src/font/library.zig b/vendor/libghostty-vt/src/font/library.zig index 56946343..49c2e880 100644 --- a/vendor/libghostty-vt/src/font/library.zig +++ b/vendor/libghostty-vt/src/font/library.zig @@ -30,7 +30,7 @@ pub const FreetypeLibrary = struct { /// Mutex to be held any time the library is /// being used to create or destroy a face. - mutex: *std.Thread.Mutex, + mutex: *std.Io.Mutex, pub const InitError = freetype.Error || Allocator.Error; @@ -38,8 +38,8 @@ pub const FreetypeLibrary = struct { const lib = try freetype.Library.init(); errdefer lib.deinit(); - const mutex = try alloc.create(std.Thread.Mutex); - mutex.* = .{}; + const mutex = try alloc.create(std.Io.Mutex); + mutex.* = .init; return Library{ .lib = lib, .alloc = alloc, .mutex = mutex }; } diff --git a/vendor/libghostty-vt/src/font/opentype/glyf.zig b/vendor/libghostty-vt/src/font/opentype/glyf.zig index 912b5fe8..d95b7ece 100644 --- a/vendor/libghostty-vt/src/font/opentype/glyf.zig +++ b/vendor/libghostty-vt/src/font/opentype/glyf.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const sfnt = @import("sfnt.zig"); +const compat_reader = @import("../../lib/compat/reader.zig"); /// Glyph Data Table /// @@ -245,11 +246,10 @@ pub const Glyf = struct { /// /// The lifetime of this struct, then, is the same as the /// lifetime of the data that is used to initialize it. - pub fn init(data: []const u8) error{EndOfStream}!Entry { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); - const header = try reader.readStructEndian(Header, .big); - return .{ .header = header, .data = data[fbs.pos..] }; + pub fn init(data: []const u8) std.Io.Reader.Error!Entry { + var reader: std.Io.Reader = .fixed(data); + const header = try compat_reader.readStructEndian(&reader, Header, .big); + return .{ .header = header, .data = data[@sizeOf(Header)..] }; } /// Identifies what type (simple or composite) of entry this is. @@ -262,10 +262,6 @@ pub const Glyf = struct { /// Errors that can be returned from `Entry.size()`. pub const SizeError = error{ - /// The entry's data wasn't large enough, ran - /// out of bytes before we were done reading. - EndOfStream, - /// The entry contains hinting instructions, /// which we don't currently support. InstructionsNotSupported, @@ -283,7 +279,7 @@ pub const Glyf = struct { /// This entry defines points past the index determined /// by the final element of the endPtsOfContours array. TooManyPoints, - }; + } || std.Io.Reader.Error; /// Errors that can be returned from `Entry.decode()`. pub const DecodeError = SizeError || Allocator.Error || error{ @@ -299,8 +295,7 @@ pub const Glyf = struct { /// NOTE: Currently produces errors when given composite glyphs /// or any glyphs that have hinting instructions included. pub fn size(self: Entry) SizeError!usize { - var fbs = std.io.fixedBufferStream(self.data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(self.data); switch (self.entryType()) { // https://learn.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description .simple => { @@ -329,7 +324,7 @@ pub const Glyf = struct { // of each contour, in increasing numeric order. var max_point_index: isize = -1; for (0..num_contours) |_| { - const index = try reader.readInt(sfnt.uint16, .big); + const index = try compat_reader.readerInt(&reader, sfnt.uint16, .big); // The endpoints are supposed to monotonically increase. if (index <= max_point_index) return error.EndPointsOutOfOrder; max_point_index = index; @@ -342,7 +337,7 @@ pub const Glyf = struct { // If instructionLength is zero, no instructions // are present for this glyph, and this field is // followed directly by the flags field. - const instructions_length = try reader.readInt(sfnt.uint16, .big); + const instructions_length = try compat_reader.readerInt(&reader, sfnt.uint16, .big); // Since we don't have code that validates instruction // byte code, we just reject all glyphs that contain any. @@ -373,7 +368,7 @@ pub const Glyf = struct { var x_coords_len: usize = 0; var y_coords_len: usize = 0; while (i <= max_point_index) : (i += 1) { - const flag: SimpleFlags = @bitCast(try reader.readByte()); + const flag: SimpleFlags = @bitCast(try compat_reader.readByte(&reader)); // Determine how many bytes the x and y coordinates will // be represented with in the corresponding arrays, add @@ -393,7 +388,7 @@ pub const Glyf = struct { // that count, and the x_coords_len and y_coords_len // must be increased by the correct number of bytes // as well. - const repeat_count: usize = try reader.readByte(); + const repeat_count: usize = try compat_reader.readByte(&reader); i += repeat_count; x_coords_len += repeat_count * flag.xBytes(); y_coords_len += repeat_count * flag.yBytes(); @@ -415,7 +410,7 @@ pub const Glyf = struct { // We determined the length of this section (in bytes) // above while processing the flags, so that we can just // skip that many bytes to validate this field. - try reader.skipBytes(x_coords_len, .{}); + try compat_reader.readSkipBytes(&reader, x_coords_len); // uint8 or int16 yCoordinates[variable] // @@ -426,7 +421,7 @@ pub const Glyf = struct { // We determined the length of this section (in bytes) // above while processing the flags, so that we can just // skip that many bytes to validate this field. - try reader.skipBytes(y_coords_len, .{}); + try compat_reader.readSkipBytes(&reader, y_coords_len); }, .composite => { @@ -441,7 +436,7 @@ pub const Glyf = struct { } // No issues found, the glyf entry is valid, return its length. - return @sizeOf(Header) + fbs.pos; + return @sizeOf(Header) + reader.seek; } /// Decode this simple glyph entry into an owned outline. @@ -455,8 +450,7 @@ pub const Glyf = struct { .composite => return error.CompositeNotSupported, } - var fbs = std.io.fixedBufferStream(self.data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(self.data); // A zero-contour glyph may be header-only. See size for the // reason for the hardcoded 2 here. @@ -473,7 +467,7 @@ pub const Glyf = struct { // If we have no contours, then the only possible remaining // field is instructionLength. Instructions are not supported. if (num_contours == 0) { - const instructions_length = try reader.readInt(sfnt.uint16, .big); + const instructions_length = try compat_reader.readerInt(&reader, sfnt.uint16, .big); if (instructions_length > 0) return error.InstructionsNotSupported; return .{ .points = &.{}, .contours = end_points }; } @@ -488,7 +482,7 @@ pub const Glyf = struct { // with the valid index. The final endpoint tells us our point // count, since endpoints are stored as inclusive point indices. for (0..end_points.len) |i| { - const index = try reader.readInt(sfnt.uint16, .big); + const index = try compat_reader.readerInt(&reader, sfnt.uint16, .big); if (index <= prev_end_point) return error.EndPointsOutOfOrder; prev_end_point = index; end_points[i] = index; @@ -499,7 +493,7 @@ pub const Glyf = struct { }; // Instructions are not supported. - const instructions_length = try reader.readInt(sfnt.uint16, .big); + const instructions_length = try compat_reader.readerInt(&reader, sfnt.uint16, .big); if (instructions_length > 0) return error.InstructionsNotSupported; // Allocate our points right away even though the next entries @@ -522,12 +516,12 @@ pub const Glyf = struct { { var point_i: usize = 0; while (point_i < point_count) { - const flag: SimpleFlags = @bitCast(try reader.readByte()); + const flag: SimpleFlags = @bitCast(try compat_reader.readByte(&reader)); flags[point_i] = flag; point_i += 1; if (flag.repeat) { - const repeat_count: usize = try reader.readByte(); + const repeat_count: usize = try compat_reader.readByte(&reader); if (point_i + repeat_count > point_count) return error.TooManyPoints; for (0..repeat_count) |_| { @@ -543,11 +537,11 @@ pub const Glyf = struct { for (flags, points) |flag, *point| { const dx: i32 = if (flag.x_short) short: { break :short if (flag.x_repeat_or_sign) - @as(i32, try reader.readByte()) + @as(i32, try compat_reader.readByte(&reader)) else - -@as(i32, try reader.readByte()); + -@as(i32, try compat_reader.readByte(&reader)); } else if (!flag.x_repeat_or_sign) - @as(i32, try reader.readInt(sfnt.int16, .big)) + @as(i32, try compat_reader.readerInt(&reader, sfnt.int16, .big)) else 0; @@ -564,11 +558,11 @@ pub const Glyf = struct { for (flags, points) |flag, *point| { const dy: i32 = if (flag.y_short) short: { break :short if (flag.y_repeat_or_sign) - @as(i32, try reader.readByte()) + @as(i32, try compat_reader.readByte(&reader)) else - -@as(i32, try reader.readByte()); + -@as(i32, try compat_reader.readByte(&reader)); } else if (!flag.y_repeat_or_sign) - @as(i32, try reader.readInt(sfnt.int16, .big)) + @as(i32, try compat_reader.readerInt(&reader, sfnt.int16, .big)) else 0; @@ -599,7 +593,7 @@ pub const Glyf = struct { } /// Retrieve the entry at the provided offset. - pub fn entry(self: Glyf, index: usize) error{EndOfStream}!Entry { + pub fn entry(self: Glyf, index: usize) std.Io.Reader.Error!Entry { return try Entry.init(self.data[index..]); } }; diff --git a/vendor/libghostty-vt/src/font/opentype/head.zig b/vendor/libghostty-vt/src/font/opentype/head.zig index 38284d9c..009fd144 100644 --- a/vendor/libghostty-vt/src/font/opentype/head.zig +++ b/vendor/libghostty-vt/src/font/opentype/head.zig @@ -1,5 +1,6 @@ const std = @import("std"); const sfnt = @import("sfnt.zig"); +const compat_reader = @import("../../lib/compat/reader.zig"); /// Font Header Table /// @@ -134,10 +135,9 @@ pub const Head = extern struct { glyphDataFormat: sfnt.int16 align(1), /// Parse the table from raw data. - pub fn init(data: []const u8) error{EndOfStream}!Head { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); - return try reader.readStructEndian(Head, .big); + pub fn init(data: []const u8) std.Io.Reader.Error!Head { + var reader: std.Io.Reader = .fixed(data); + return try compat_reader.readStructEndian(&reader, Head, .big); } }; diff --git a/vendor/libghostty-vt/src/font/opentype/hhea.zig b/vendor/libghostty-vt/src/font/opentype/hhea.zig index b2b3f3e2..7ad77054 100644 --- a/vendor/libghostty-vt/src/font/opentype/hhea.zig +++ b/vendor/libghostty-vt/src/font/opentype/hhea.zig @@ -1,5 +1,6 @@ const std = @import("std"); const sfnt = @import("sfnt.zig"); +const compat_reader = @import("../../lib/compat/reader.zig"); /// Horizontal Header Table /// @@ -71,10 +72,8 @@ pub const Hhea = extern struct { /// Parse the table from raw data. pub fn init(data: []const u8) !Hhea { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); - - return try reader.readStructEndian(Hhea, .big); + var reader: std.Io.Reader = .fixed(data); + return try compat_reader.readStructEndian(&reader, Hhea, .big); } }; diff --git a/vendor/libghostty-vt/src/font/opentype/os2.zig b/vendor/libghostty-vt/src/font/opentype/os2.zig index 1cd11f35..5bb0b42b 100644 --- a/vendor/libghostty-vt/src/font/opentype/os2.zig +++ b/vendor/libghostty-vt/src/font/opentype/os2.zig @@ -1,5 +1,6 @@ const std = @import("std"); const sfnt = @import("sfnt.zig"); +const compat_reader = @import("../../lib/compat/reader.zig"); pub const FSSelection = packed struct(sfnt.uint16) { /// Font contains italic or oblique glyphs, otherwise they are upright. @@ -348,21 +349,17 @@ pub const OS2 = struct { usUpperOpticalPointSize: ?u16 = null, /// Parse the table from raw data. - pub fn init(data: []const u8) error{ - EndOfStream, - OS2VersionNotSupported, - }!OS2 { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + pub fn init(data: []const u8) (error{OS2VersionNotSupported} || std.Io.Reader.Error)!OS2 { + var reader: std.Io.Reader = .fixed(data); - const version = try reader.readInt(sfnt.uint16, .big); + const version = try compat_reader.readerInt(&reader, sfnt.uint16, .big); // Return to the start, cause the version is part of the struct. - try fbs.seekTo(0); + reader.seek = 0; switch (version) { 5 => { - const table = try reader.readStructEndian(OS2v5, .big); + const table = try compat_reader.readStructEndian(&reader, OS2v5, .big); return .{ .version = table.version, .xAvgCharWidth = table.xAvgCharWidth, @@ -406,7 +403,7 @@ pub const OS2 = struct { }; }, 4, 3, 2 => { - const table = try reader.readStructEndian(OS2v4_3_2, .big); + const table = try compat_reader.readStructEndian(&reader, OS2v4_3_2, .big); return .{ .version = table.version, .xAvgCharWidth = table.xAvgCharWidth, @@ -448,7 +445,7 @@ pub const OS2 = struct { }; }, 1 => { - const table = try reader.readStructEndian(OS2v1, .big); + const table = try compat_reader.readStructEndian(&reader, OS2v1, .big); return .{ .version = table.version, .xAvgCharWidth = table.xAvgCharWidth, @@ -485,7 +482,7 @@ pub const OS2 = struct { }; }, 0 => { - const table = try reader.readStructEndian(OS2v0, .big); + const table = try compat_reader.readStructEndian(&reader, OS2v0, .big); return .{ .version = table.version, .xAvgCharWidth = table.xAvgCharWidth, diff --git a/vendor/libghostty-vt/src/font/opentype/post.zig b/vendor/libghostty-vt/src/font/opentype/post.zig index 8031a0a4..776bf8a3 100644 --- a/vendor/libghostty-vt/src/font/opentype/post.zig +++ b/vendor/libghostty-vt/src/font/opentype/post.zig @@ -1,5 +1,6 @@ const std = @import("std"); const sfnt = @import("sfnt.zig"); +const compat_reader = @import("../../lib/compat/reader.zig"); /// PostScript Table /// @@ -46,10 +47,9 @@ pub const Post = extern struct { maxMemType1: sfnt.uint32 align(1), /// Parse the table from raw data. - pub fn init(data: []const u8) error{EndOfStream}!Post { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); - return try reader.readStructEndian(Post, .big); + pub fn init(data: []const u8) std.Io.Reader.Error!Post { + var reader: std.Io.Reader = .fixed(data); + return try compat_reader.readStructEndian(&reader, Post, .big); } }; diff --git a/vendor/libghostty-vt/src/font/opentype/sfnt.zig b/vendor/libghostty-vt/src/font/opentype/sfnt.zig index 55a9a0bd..8b182e69 100644 --- a/vendor/libghostty-vt/src/font/opentype/sfnt.zig +++ b/vendor/libghostty-vt/src/font/opentype/sfnt.zig @@ -235,8 +235,7 @@ pub const SFNT = struct { /// Parse a font from raw data. The struct will keep a /// reference to `data` and use it for future operations. pub fn init(data: []const u8, alloc: Allocator) !SFNT { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(data); // SFNT files use big endian, if our native endian is // not big we'll need to byte swap the values we read. @@ -244,7 +243,7 @@ pub const SFNT = struct { var directory: Directory = undefined; - try reader.readNoEof(std.mem.asBytes(&directory.offset)); + try reader.readSliceAll(std.mem.asBytes(&directory.offset)); if (byte_swap) std.mem.byteSwapAllFields( Directory.OffsetSubtable, &directory.offset, @@ -252,7 +251,7 @@ pub const SFNT = struct { directory.records = try alloc.alloc(Directory.TableRecord, directory.offset.num_tables); - try reader.readNoEof(std.mem.sliceAsBytes(directory.records)); + try reader.readSliceAll(std.mem.sliceAsBytes(directory.records)); if (byte_swap) for (directory.records) |*record| { std.mem.byteSwapAllFields( Directory.TableRecord, diff --git a/vendor/libghostty-vt/src/font/opentype/svg.zig b/vendor/libghostty-vt/src/font/opentype/svg.zig index b4d9ccaa..9115fe62 100644 --- a/vendor/libghostty-vt/src/font/opentype/svg.zig +++ b/vendor/libghostty-vt/src/font/opentype/svg.zig @@ -1,5 +1,6 @@ const std = @import("std"); const font = @import("../main.zig"); +const compat_reader = @import("../../lib/compat/reader.zig"); /// SVG glyphs description table. /// @@ -21,27 +22,24 @@ pub const SVG = struct { /// All records in the table. records: []const [12]u8, - pub fn init(data: []const u8) error{ - EndOfStream, - SVGVersionNotSupported, - }!SVG { - var fbs = std.io.fixedBufferStream(data); - const reader = fbs.reader(); + pub fn init(data: []const u8) (error{SVGVersionNotSupported} || std.Io.Reader.Error)!SVG { + var reader: std.Io.Reader = .fixed(data); // Version - if (try reader.readInt(u16, .big) != 0) { + if (try compat_reader.readerInt(&reader, u16, .big) != 0) { return error.SVGVersionNotSupported; } // Offset - const offset = try reader.readInt(u32, .big); + const offset = try compat_reader.readerInt(&reader, u32, .big); // Seek to the offset to get our document list - try fbs.seekTo(offset); + if (offset > data.len) return error.EndOfStream; + reader.seek = offset; // Get our document records along with the start/end glyph range. - const len = try reader.readInt(u16, .big); - const records: [*]const [12]u8 = @ptrCast(data[try fbs.getPos()..]); + const len = try compat_reader.readerInt(&reader, u16, .big); + const records: [*]const [12]u8 = @ptrCast(data[reader.seek..]); const start_range = try glyphRange(&records[0]); const end_range = if (len == 1) start_range else try glyphRange(&records[(len - 1)]); @@ -84,11 +82,10 @@ pub const SVG = struct { } fn glyphRange(record: []const u8) !struct { u16, u16 } { - var fbs = std.io.fixedBufferStream(record); - const reader = fbs.reader(); + var reader: std.Io.Reader = .fixed(record); return .{ - try reader.readInt(u16, .big), - try reader.readInt(u16, .big), + try compat_reader.readerInt(&reader, u16, .big), + try compat_reader.readerInt(&reader, u16, .big), }; } }; @@ -112,3 +109,13 @@ test "SVG" { try testing.expectEqual(11482, svg.end_glyph_id); try testing.expect(svg.hasGlyph(11482)); } + +test "SVG document list offset past end" { + const testing = std.testing; + const table = [_]u8{ + 0, 0, // Version + 0, 0, 0, 7, // Document list offset, one byte past end + }; + + try testing.expectError(error.EndOfStream, SVG.init(&table)); +} diff --git a/vendor/libghostty-vt/src/font/shaper/coretext.zig b/vendor/libghostty-vt/src/font/shaper/coretext.zig index 8a9374bd..5172744d 100644 --- a/vendor/libghostty-vt/src/font/shaper/coretext.zig +++ b/vendor/libghostty-vt/src/font/shaper/coretext.zig @@ -20,6 +20,7 @@ const SharedGrid = font.SharedGrid; const Style = font.Style; const Presentation = font.Presentation; const CFReleaseThread = os.CFReleaseThread; +const global = @import("../../global.zig"); const log = std.log.scoped(.font_shaper); @@ -90,7 +91,7 @@ pub const Shaper = struct { unichars: std.ArrayListUnmanaged(u16), fn init() RunState { - return .{ .codepoints = .{}, .unichars = .{} }; + return .{ .codepoints = .empty, .unichars = .empty }; } fn deinit(self: *RunState, alloc: Allocator) void { @@ -213,18 +214,18 @@ pub const Shaper = struct { CFReleaseThread.threadMain, .{cf_release_thread}, ); - cf_release_thr.setName("cf_release") catch {}; + cf_release_thr.setName(global.io(), "cf_release") catch {}; return .{ .alloc = alloc, - .cell_buf = .{}, + .cell_buf = .empty, .run_state = run_state, .features = features, .features_no_default = features_no_default, .typesetter_attr_dict = typesetter_attr_dict, - .cached_fonts = .{}, + .cached_fonts = .empty, .cached_font_grid = 0, - .cf_release_pool = .{}, + .cf_release_pool = .empty, .cf_release_thread = cf_release_thread, .cf_release_thr = cf_release_thr, }; @@ -282,7 +283,7 @@ pub const Shaper = struct { // Send the items. If the send succeeds then we wake up the // thread to process the items. If the send fails then do a manual // cleanup. - if (self.cf_release_thread.mailbox.push(.{ .release = .{ + if (self.cf_release_thread.mailbox.push(global.io(), .{ .release = .{ .refs = items, .alloc = self.alloc, } }, .{ .forever = {} }) != 0) { @@ -360,7 +361,7 @@ pub const Shaper = struct { // Make room for the attributed string, CTTypesetter, and CTLine. try self.cf_release_pool.ensureUnusedCapacity(self.alloc, 4); - const str = macos.foundation.String.createWithCharactersNoCopy(state.unichars.items); + const str = try macos.foundation.String.createWithCharactersNoCopy(state.unichars.items); self.cf_release_pool.appendAssumeCapacity(str); // Create an attributed string from our string @@ -380,7 +381,7 @@ pub const Shaper = struct { self.cf_release_pool.appendAssumeCapacity(typesetter); // Create a line from the typesetter - const line = typesetter.createLine(.{ .location = 0, .length = 0 }); + const line = try typesetter.createLine(.{ .location = 0, .length = 0 }); self.cf_release_pool.appendAssumeCapacity(line); // This keeps track of the current x offset (sum of advance.width) and @@ -606,8 +607,8 @@ pub const Shaper = struct { // // Because of this, we only acquire the read lock to grab the // face and set it up, then release it. - grid.lock.lockShared(); - defer grid.lock.unlockShared(); + grid.lock.lockSharedUncancelable(global.io()); + defer grid.lock.unlockShared(global.io()); const face = try grid.resolver.collection.getFace(index); const original = face.font; @@ -842,7 +843,7 @@ test "run iterator" { defer testdata.deinit(); { - var t: terminal.Terminal = try .init(alloc, .{ + var t: terminal.Terminal = try .init(testing.io, alloc, .{ .cols = 5, .rows = 3, }); @@ -869,7 +870,7 @@ test "run iterator" { // Spaces should be part of a run { - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -892,7 +893,7 @@ test "run iterator" { { // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -917,7 +918,7 @@ test "run iterator" { // Bad ligatures for (&[_][]const u8{ "fl", "fi", "st" }) |bad| { // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -949,7 +950,7 @@ test "run iterator: empty cells with background set" { { // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1009,7 +1010,7 @@ test "shape" { buf_idx += try std.unicode.utf8Encode(0x1F3FD, buf[buf_idx..]); // Medium skin tone // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1048,7 +1049,7 @@ test "shape nerd fonts" { buf_idx += try std.unicode.utf8Encode(' ', buf[buf_idx..]); // space // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1081,7 +1082,7 @@ test "shape inconsolata ligs" { defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1110,7 +1111,7 @@ test "shape inconsolata ligs" { } { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1147,7 +1148,7 @@ test "shape monaspace ligs" { defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1185,7 +1186,7 @@ test "shape left-replaced lig in last run" { defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1223,7 +1224,7 @@ test "shape left-replaced lig in early run" { defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1258,7 +1259,7 @@ test "shape U+3C9 with JB Mono" { defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1295,7 +1296,7 @@ test "shape emoji width" { defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1330,7 +1331,7 @@ test "shape emoji width long" { defer testdata.deinit(); // Make a screen and add a long emoji sequence to it. - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); var page = t.screens.active.pages.pages.first.?.page(); @@ -1338,7 +1339,7 @@ test "shape emoji width long" { const cell = &row.cells.ptr(page.memory)[0]; cell.* = .{ .content_tag = .codepoint, - .content = .{ .codepoint = 0x1F9D4 }, // Person with beard + .content = .{ .codepoint = .{ .data = 0x1F9D4 } }, // Person with beard }; var graphemes = [_]u21{ 0x1F3FB, // Light skin tone (Fitz 1-2) @@ -1385,7 +1386,7 @@ test "shape variation selector VS15" { buf_idx += try std.unicode.utf8Encode(0xFE0E, buf[buf_idx..]); // ZWJ to force text // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1424,7 +1425,7 @@ test "shape variation selector VS16" { buf_idx += try std.unicode.utf8Encode(0xFE0F, buf[buf_idx..]); // ZWJ to force color // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1458,7 +1459,7 @@ test "shape with empty cells in between" { defer testdata.deinit(); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1502,7 +1503,7 @@ test "shape Combining characters" { buf_idx += try std.unicode.utf8Encode('a', buf[buf_idx..]); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1552,7 +1553,7 @@ test "shape Devanagari string" { defer testdata.deinit(); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Disable grapheme clustering @@ -1611,7 +1612,7 @@ test "shape Tai Tham vowels (position differs from advance)" { buf_idx += try std.unicode.utf8Encode(0x1a70, buf[buf_idx..]); // ᩰ // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1672,7 +1673,7 @@ test "shape Tai Tham letters (position.y differs from advance)" { buf_idx += try std.unicode.utf8Encode(0x1a69, buf[buf_idx..]); // U // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1732,7 +1733,7 @@ test "shape Javanese ligatures" { buf_idx += try std.unicode.utf8Encode(0xa9b8, buf[buf_idx..]); // Vowel sign SUKU // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1795,7 +1796,7 @@ test "shape Chakma vowel sign with ligature (vowel sign renders first)" { buf_idx += try std.unicode.utf8Encode(0x1112c, buf[buf_idx..]); // Vowel Sign U // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1866,7 +1867,7 @@ test "shape Bengali ligatures with out of order vowels" { buf_idx += try std.unicode.utf8Encode(0x09c7, buf[buf_idx..]); // Vowel sign E // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1924,7 +1925,7 @@ test "shape box glyphs" { buf_idx += try std.unicode.utf8Encode(0x2501, buf[buf_idx..]); // // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1962,7 +1963,7 @@ test "shape selection boundary" { defer testdata.deinit(); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -2067,7 +2068,7 @@ test "shape cursor boundary" { defer testdata.deinit(); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -2204,7 +2205,7 @@ test "shape cursor boundary and colored emoji" { defer testdata.deinit(); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -2301,7 +2302,7 @@ test "shape cell attribute change" { // Plain >= should shape into 1 run { - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -2327,7 +2328,7 @@ test "shape cell attribute change" { // Bold vs regular should split { - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -2355,7 +2356,7 @@ test "shape cell attribute change" { // Changing fg color should split { - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -2386,7 +2387,7 @@ test "shape cell attribute change" { // Changing bg color should NOT split { - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -2417,7 +2418,7 @@ test "shape cell attribute change" { // Same bg color should not split { - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -2462,7 +2463,7 @@ test "shape high plane sprite font codepoint" { var testdata = try testShaper(alloc); defer testdata.deinit(); - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(testing.io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); diff --git a/vendor/libghostty-vt/src/font/shaper/feature.zig b/vendor/libghostty-vt/src/font/shaper/feature.zig index 5bd73f97..c60eb049 100644 --- a/vendor/libghostty-vt/src/font/shaper/feature.zig +++ b/vendor/libghostty-vt/src/font/shaper/feature.zig @@ -1,6 +1,7 @@ const std = @import("std"); const assert = @import("../../quirks.zig").inlineAssert; const Allocator = std.mem.Allocator; +const compat_reader = @import("../../lib/compat/reader.zig"); const log = std.log.scoped(.font_shaper); @@ -18,9 +19,8 @@ pub const Feature = struct { value: u32, pub fn fromString(str: []const u8) ?Feature { - var fbs = std.io.fixedBufferStream(str); - const reader = fbs.reader(); - return .fromReader(reader); + var reader: std.Io.Reader = .fixed(str); + return .fromReader(&reader); } /// Parse a single font feature setting from a std.io.Reader, with a version @@ -55,7 +55,7 @@ pub const Feature = struct { done, }).start) { // If we're done then we skip whitespace until we see a ','. - .done => while (true) switch (reader.readByte() catch ',') { + .done => while (true) switch (compat_reader.readByte(reader) catch ',') { ' ', '\t' => continue, ',' => break, // If we see something other than whitespace or a ',' @@ -66,11 +66,11 @@ pub const Feature = struct { // If we're fast-forwarding from an error we just wanna // stop at the first boundary and ignore all other bytes. .err => { - reader.skipUntilDelimiterOrEof(',') catch {}; + compat_reader.readSkipUntilDelimiterOrEof(reader, ',') catch {}; return null; }, - .start => while (true) switch (reader.readByte() catch ',') { + .start => while (true) switch (compat_reader.readByte(reader) catch ',') { // Ignore leading whitespace. ' ', '\t' => continue, // Empty feature string. @@ -97,7 +97,7 @@ pub const Feature = struct { }, }, - .tag => while (true) switch (reader.readByte() catch ',') { + .tag => while (true) switch (compat_reader.readByte(reader) catch ',') { // If the tag is interrupted by a comma it's invalid. ',' => return null, // Ignore quote marks. This does technically ignore cases like @@ -112,7 +112,7 @@ pub const Feature = struct { }, }, - .space => while (true) switch (reader.readByte() catch ',') { + .space => while (true) switch (compat_reader.readByte(reader) catch ',') { ' ', '\t' => continue, // Ignore quote marks since we might have a // closing quote from the tag still ahead. @@ -142,7 +142,7 @@ pub const Feature = struct { else => continue :state .err, }, - .int => while (true) switch (reader.readByte() catch ',') { + .int => while (true) switch (compat_reader.readByte(reader) catch ',') { ',' => break, '0'...'9' => |byte| { // If our value gets too big while @@ -155,7 +155,7 @@ pub const Feature = struct { else => continue :state .err, }, - .bool => while (true) switch (reader.readByte() catch ',') { + .bool => while (true) switch (compat_reader.readByte(reader) catch ',') { ',' => return null, 'n', 'N' => { // "ofn" @@ -222,7 +222,7 @@ pub const Feature = struct { /// A list of font feature settings (see `Feature` for more documentation). pub const FeatureList = struct { - features: std.ArrayListUnmanaged(Feature) = .{}, + features: std.ArrayList(Feature) = .empty, pub fn deinit(self: *FeatureList, alloc: Allocator) void { self.features.deinit(alloc); @@ -243,15 +243,14 @@ pub const FeatureList = struct { alloc: Allocator, str: []const u8, ) !void { - var fbs = std.io.fixedBufferStream(str); - const reader = fbs.reader(); - while (fbs.pos < fbs.buffer.len) { - const i = fbs.pos; - if (Feature.fromReader(reader)) |feature| { + var reader: std.Io.Reader = .fixed(str); + while (reader.seek < reader.end) { + const i = reader.seek; + if (Feature.fromReader(&reader)) |feature| { try self.features.append(alloc, feature); } else log.warn( "failed to parse font feature setting: \"{s}\"", - .{fbs.buffer[i..fbs.pos]}, + .{reader.buffer[i..reader.seek]}, ); } } diff --git a/vendor/libghostty-vt/src/font/shaper/harfbuzz.zig b/vendor/libghostty-vt/src/font/shaper/harfbuzz.zig index 996aabff..11f9fd23 100644 --- a/vendor/libghostty-vt/src/font/shaper/harfbuzz.zig +++ b/vendor/libghostty-vt/src/font/shaper/harfbuzz.zig @@ -15,6 +15,7 @@ const Library = font.Library; const SharedGrid = font.SharedGrid; const Style = font.Style; const Presentation = font.Presentation; +const global = @import("../../global.zig"); const log = std.log.scoped(.font_shaper); @@ -36,7 +37,7 @@ pub const Shaper = struct { /// The codepoints added to the buffer before shaping. We need to keep /// these separately because after shaping, HarfBuzz replaces codepoints /// with glyph indices in the buffer. - codepoints: std.ArrayListUnmanaged(Codepoint) = .{}, + codepoints: std.ArrayList(Codepoint) = .empty, const Codepoint = struct { cluster: u32, @@ -87,7 +88,7 @@ pub const Shaper = struct { return Shaper{ .alloc = alloc, .hb_buf = try harfbuzz.Buffer.create(), - .cell_buf = .{}, + .cell_buf = .empty, .hb_feats = hb_feats, }; } @@ -134,8 +135,8 @@ pub const Shaper = struct { // We have to lock the grid to get the face and unfortunately // freetype faces (typically used with harfbuzz) are not thread // safe so this has to be an exclusive lock. - run.grid.lock.lock(); - defer run.grid.lock.unlock(); + run.grid.lock.lockUncancelable(global.io()); + defer run.grid.lock.unlock(global.io()); const face = try run.grid.resolver.collection.getFace(run.font_index); const i = if (!face.quirks_disable_default_font_features) 0 else i: { @@ -437,13 +438,14 @@ pub const Shaper = struct { test "run iterator" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); { // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -467,7 +469,7 @@ test "run iterator" { // Spaces should be part of a run { - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -490,7 +492,7 @@ test "run iterator" { { // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -521,13 +523,14 @@ test "run iterator" { test "run iterator: empty cells with background set" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); { // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -576,6 +579,7 @@ test "run iterator: empty cells with background set" { test "shape" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); @@ -587,7 +591,7 @@ test "shape" { buf_idx += try std.unicode.utf8Encode(0x1F3FD, buf[buf_idx..]); // Medium skin tone // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -616,12 +620,13 @@ test "shape" { test "shape inconsolata ligs" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -650,7 +655,7 @@ test "shape inconsolata ligs" { } { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -682,12 +687,13 @@ test "shape inconsolata ligs" { test "shape monaspace ligs" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaperWithFont(alloc, .monaspace_neon); defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -723,11 +729,12 @@ test "shape monaspace ligs" { test "shape arabic forced LTR" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaperWithFont(alloc, .arabic); defer testdata.deinit(); - var t = try terminal.Terminal.init(alloc, .{ .cols = 120, .rows = 30 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 120, .rows = 30 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -763,12 +770,13 @@ test "shape arabic forced LTR" { test "shape emoji width" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); { - var t = try terminal.Terminal.init(alloc, .{ .cols = 5, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -800,12 +808,14 @@ test "shape emoji width" { test "shape emoji width long" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); // Make a screen and add a long emoji sequence to it. var t = try terminal.Terminal.init( + io, alloc, .{ .cols = 30, .rows = 3 }, ); @@ -816,7 +826,7 @@ test "shape emoji width long" { const cell = &row.cells.ptr(page.memory)[0]; cell.* = .{ .content_tag = .codepoint, - .content = .{ .codepoint = 0x1F9D4 }, // Person with beard + .content = .{ .codepoint = @bitCast(@as(u24, 0x1F9D4)) }, // Person with beard }; var graphemes = [_]u21{ 0x1F3FB, // Light skin tone (Fitz 1-2) @@ -852,9 +862,45 @@ test "shape emoji width long" { try testing.expectEqual(@as(usize, 1), count); } +test "shaper selects font for entire grapheme" { + const testing = std.testing; + const alloc = testing.allocator; + const io = testing.io; + + var testdata = try testShaper(alloc); + defer testdata.deinit(); + + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 5, .rows = 3 }); + defer t.deinit(alloc); + + var s = t.vtStream(); + defer s.deinit(); + s.nextSlice("#\u{20E3}"); // Combining enclosing keycap + + const primary = (try testdata.grid.getIndex(alloc, '#', .regular, null)).?; + try testing.expect(testdata.grid.hasCodepoint(primary, '#', null)); + try testing.expect(!testdata.grid.hasCodepoint(primary, 0x20E3, null)); + + const additional = (try testdata.grid.getIndex(alloc, 0x20E3, .regular, null)).?; + try testing.expect(testdata.grid.hasCodepoint(additional, '#', null)); + try testing.expect(testdata.grid.hasCodepoint(additional, 0x20E3, null)); + + var state: terminal.RenderState = .empty; + defer state.deinit(alloc); + try state.update(alloc, &t); + + var it = testdata.shaper.runIterator(.{ + .grid = testdata.grid, + .cells = state.row_data.get(0).cells.slice(), + }); + const run = (try it.next(alloc)).?; + try testing.expectEqual(additional, run.font_index); +} + test "shape variation selector VS15" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); @@ -865,7 +911,7 @@ test "shape variation selector VS15" { buf_idx += try std.unicode.utf8Encode(0xFE0E, buf[buf_idx..]); // ZWJ to force text // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -896,6 +942,7 @@ test "shape variation selector VS15" { test "shape variation selector VS16" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); @@ -906,7 +953,7 @@ test "shape variation selector VS16" { buf_idx += try std.unicode.utf8Encode(0xFE0F, buf[buf_idx..]); // ZWJ to force color // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -937,12 +984,14 @@ test "shape variation selector VS16" { test "shape with empty cells in between" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); // Make a screen with some data var t = try terminal.Terminal.init( + io, alloc, .{ .cols = 30, .rows = 3 }, ); @@ -977,6 +1026,7 @@ test "shape with empty cells in between" { test "shape Combining characters" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); @@ -990,6 +1040,7 @@ test "shape Combining characters" { // Make a screen with some data var t = try terminal.Terminal.init( + io, alloc, .{ .cols = 30, .rows = 3 }, ); @@ -1029,6 +1080,7 @@ test "shape Combining characters" { test "shape Devanagari string" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; // We need a font that supports devanagari for this to work, if we can't // find Arial Unicode MS, which is a system font on macOS, we just skip @@ -1040,7 +1092,7 @@ test "shape Devanagari string" { defer testdata.deinit(); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Disable grapheme clustering @@ -1087,6 +1139,7 @@ test "shape Tai Tham vowels (position differs from advance)" { // // behavior. // const testing = std.testing; // const alloc = testing.allocator; + // const io = testing.io; // // We need a font that supports Tai Tham for this to work, if we can't find // // Noto Sans Tai Tham, which is a system font on macOS, we just skip the @@ -1103,7 +1156,7 @@ test "shape Tai Tham vowels (position differs from advance)" { // buf_idx += try std.unicode.utf8Encode(0x1a70, buf[buf_idx..]); // ᩰ // // Make a screen with some data - // var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + // var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 }); // defer t.deinit(alloc); // // Enable grapheme clustering @@ -1147,6 +1200,7 @@ test "shape Tai Tham vowels (position differs from advance)" { test "shape Tibetan characters" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; // We need a font that has multiple glyphs for this codepoint to reproduce // the old broken behavior, and Noto Serif Tibetan is one of them. It's not @@ -1162,7 +1216,7 @@ test "shape Tibetan characters" { buf_idx += try std.unicode.utf8Encode(0x0f00, buf[buf_idx..]); // ༀ // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1203,6 +1257,7 @@ test "shape Tai Tham letters (run_offset.y differs from zero)" { return error.SkipZigTest; // const testing = std.testing; // const alloc = testing.allocator; + // const io = testing.io; // // We need a font that supports Tai Tham for this to work, if we can't find // // Noto Sans Tai Tham, which is a system font on macOS, we just skip the @@ -1224,7 +1279,7 @@ test "shape Tai Tham letters (run_offset.y differs from zero)" { // buf_idx += try std.unicode.utf8Encode(0x1a69, buf[buf_idx..]); // U // // Make a screen with some data - // var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + // var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 }); // defer t.deinit(alloc); // // Enable grapheme clustering @@ -1266,6 +1321,7 @@ test "shape Javanese ligatures" { return error.SkipZigTest; // const testing = std.testing; // const alloc = testing.allocator; + // const io = testing.io; // // We need a font that supports Javanese for this to work, if we can't find // // Noto Sans Javanese Regular, which is a system font on macOS, we just @@ -1287,7 +1343,7 @@ test "shape Javanese ligatures" { // buf_idx += try std.unicode.utf8Encode(0xa9b8, buf[buf_idx..]); // Vowel sign SUKU // // Make a screen with some data - // var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + // var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 }); // defer t.deinit(alloc); // // Enable grapheme clustering @@ -1327,6 +1383,7 @@ test "shape Javanese ligatures" { test "shape Chakma vowel sign with ligature (vowel sign renders first)" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; // We need a font that supports Chakma for this to work, if we can't find // Noto Sans Chakma Regular, which is a system font on macOS, we just skip @@ -1350,7 +1407,7 @@ test "shape Chakma vowel sign with ligature (vowel sign renders first)" { buf_idx += try std.unicode.utf8Encode(0x1112c, buf[buf_idx..]); // Vowel Sign U // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1398,6 +1455,7 @@ test "shape Bengali ligatures with out of order vowels" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; // We need a font that supports Bengali for this to work, if we can't find // Arial Unicode MS, which is a system font on macOS, we just skip the @@ -1425,7 +1483,7 @@ test "shape Bengali ligatures with out of order vowels" { buf_idx += try std.unicode.utf8Encode(0x09c7, buf[buf_idx..]); // Vowel sign E // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 30, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 30, .rows = 3 }); defer t.deinit(alloc); // Enable grapheme clustering @@ -1472,6 +1530,7 @@ test "shape Bengali ligatures with out of order vowels" { test "shape box glyphs" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); @@ -1482,7 +1541,7 @@ test "shape box glyphs" { buf_idx += try std.unicode.utf8Encode(0x2501, buf[buf_idx..]); // // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1516,12 +1575,13 @@ test "shape box glyphs" { test "shape selection boundary" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1621,12 +1681,13 @@ test "shape selection boundary" { test "shape cursor boundary" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); // Make a screen with some data - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1758,12 +1819,14 @@ test "shape cursor boundary" { test "shape cursor boundary and colored emoji" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); // Make a screen with some data var t = try terminal.Terminal.init( + io, alloc, .{ .cols = 3, .rows = 10 }, ); @@ -1857,13 +1920,14 @@ test "shape cursor boundary and colored emoji" { test "shape cell attribute change" { const testing = std.testing; const alloc = testing.allocator; + const io = testing.io; var testdata = try testShaper(alloc); defer testdata.deinit(); // Plain >= should shape into 1 run { - var t = try terminal.Terminal.init(alloc, .{ .cols = 10, .rows = 3 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 10, .rows = 3 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1889,7 +1953,7 @@ test "shape cell attribute change" { // Bold vs regular should split { - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1917,7 +1981,7 @@ test "shape cell attribute change" { // Changing fg color should split { - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1948,7 +2012,7 @@ test "shape cell attribute change" { // Changing bg color should not split { - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); @@ -1979,7 +2043,7 @@ test "shape cell attribute change" { // Same bg color should not split { - var t = try terminal.Terminal.init(alloc, .{ .cols = 3, .rows = 10 }); + var t = try terminal.Terminal.init(io, alloc, .{ .cols = 3, .rows = 10 }); defer t.deinit(alloc); var s = t.vtStream(); diff --git a/vendor/libghostty-vt/src/font/shaper/run.zig b/vendor/libghostty-vt/src/font/shaper/run.zig index 45c5d38c..9f33ba32 100644 --- a/vendor/libghostty-vt/src/font/shaper/run.zig +++ b/vendor/libghostty-vt/src/font/shaper/run.zig @@ -348,47 +348,34 @@ pub const RunIterator = struct { // we just return whatever index for the cell codepoint. if (!cell.hasGrapheme()) return primary; - // If this is a grapheme, we need to find a font that supports - // all of the codepoints in the grapheme. - var candidates: std.ArrayList(font.Collection.Index) = try .initCapacity( - alloc, - graphemes.len + 1, - ); - defer candidates.deinit(alloc); - candidates.appendAssumeCapacity(primary); + // Try the primary font followed by the font selected for each + // additional codepoint until one supports the entire grapheme. + candidate: for (0..graphemes.len + 1) |i| { + const idx = if (i == 0) primary else idx: { + const cp = graphemes[i - 1]; - for (graphemes) |cp| { - // Ignore Emoji ZWJs - if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue; + // Ignore Emoji ZWJs + if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue :candidate; - // Find a font that supports this codepoint. If none support this - // then the whole grapheme can't be rendered so we return null. - // - // We explicitly do not require the additional grapheme components - // to support the base presentation, since it is common for emoji - // fonts to support the base emoji with emoji presentation but not - // certain ZWJ-combined characters like the male and female signs. - const idx = try self.opts.grid.getIndex( - alloc, - cp, - style, - null, - ) orelse return null; - candidates.appendAssumeCapacity(idx); - } + // We explicitly do not require the additional grapheme components + // to support the base presentation, since it is common for emoji + // fonts to support the base emoji with emoji presentation but not + // certain ZWJ-combined characters like the male and female signs. + break :idx try self.opts.grid.getIndex( + alloc, + cp, + style, + null, + ) orelse return null; + }; - // We need to find a candidate that has ALL of our codepoints - for (candidates.items) |idx| { if (!self.opts.grid.hasCodepoint(idx, primary_cp, presentation)) continue; for (graphemes) |cp| { // Ignore Emoji ZWJs if (cp == 0xFE0E or cp == 0xFE0F or cp == 0x200D) continue; - if (!self.opts.grid.hasCodepoint(idx, cp, null)) break; - } else { - // If the while completed, then we have a candidate that - // supports all of our codepoints. - return idx; + if (!self.opts.grid.hasCodepoint(idx, cp, null)) continue :candidate; } + return idx; } return null; diff --git a/vendor/libghostty-vt/src/font/sprite/Face.zig b/vendor/libghostty-vt/src/font/sprite/Face.zig index fd029d13..e2e37d2a 100644 --- a/vendor/libghostty-vt/src/font/sprite/Face.zig +++ b/vendor/libghostty-vt/src/font/sprite/Face.zig @@ -273,15 +273,16 @@ fn testDiffAtlas( // Get the file contents, we compare the PNG data first in // order to ensure that no one smuggles arbitrary binary // data in to the reference PNGs. - const test_file = try std.fs.openFileAbsolute(path, .{ .mode = .read_only }); - defer test_file.close(); - const test_bytes = try test_file.readToEndAlloc( + const test_file = try std.Io.Dir.openFileAbsolute(std.testing.io, path, .{ .mode = .read_only }); + defer test_file.close(std.testing.io); + var test_file_reader = test_file.reader(std.testing.io, &.{}); + const test_bytes = try test_file_reader.interface.readAlloc( alloc, - std.math.maxInt(usize), + (try test_file.stat(std.testing.io)).size, ); defer alloc.free(test_bytes); - const cwd_absolute = try std.fs.cwd().realpathAlloc(alloc, "."); + const cwd_absolute = try std.Io.Dir.cwd().realPathFileAlloc(std.testing.io, ".", alloc); defer alloc.free(cwd_absolute); // Get the reference file contents to compare. @@ -292,7 +293,7 @@ fn testDiffAtlas( ); defer alloc.free(ref_path); const ref_file = - std.fs.cwd().openFile(ref_path, .{ .mode = .read_only }) catch |err| { + std.Io.Dir.cwd().openFile(std.testing.io, ref_path, .{ .mode = .read_only }) catch |err| { log.err("Can't open reference file {s}: {}\n", .{ ref_path, err, @@ -306,14 +307,15 @@ fn testDiffAtlas( .{ cwd_absolute, i, i + 0xFF, width, height, thickness }, ); defer alloc.free(test_path); - try std.fs.copyFileAbsolute(path, test_path, .{}); + try std.Io.Dir.copyFileAbsolute(path, test_path, std.testing.io, .{}); return true; }; - defer ref_file.close(); - const ref_bytes = try ref_file.readToEndAlloc( + defer ref_file.close(std.testing.io); + var ref_file_reader = ref_file.reader(std.testing.io, &.{}); + const ref_bytes = try ref_file_reader.interface.readAlloc( alloc, - std.math.maxInt(usize), + (try ref_file.stat(std.testing.io)).size, ); defer alloc.free(ref_bytes); @@ -330,7 +332,7 @@ fn testDiffAtlas( .{ cwd_absolute, i, i + 0xFF, width, height, thickness }, ); defer alloc.free(test_path); - try std.fs.copyFileAbsolute(path, test_path, .{}); + try std.Io.Dir.copyFileAbsolute(path, test_path, std.testing.io, .{}); // Use wuffs to decode the reference PNG to raw pixels. // These will be RGBA, so when diffing we can just compare @@ -394,7 +396,7 @@ fn testDiffAtlas( .{ i, i + 0xFF, width, height, thickness }, ); defer alloc.free(diff_path); - try z2d.png_exporter.writeToPNGFile(diff, diff_path, .{}); + try z2d.png_exporter.writeToPNGFile(std.testing.io, diff, diff_path, .{}); log.err( "One or more glyphs differ from reference file in range U+{X}...U+{X}! " ++ "test={s}, reference={s}, diff={s}", @@ -462,7 +464,7 @@ fn testDrawRanges( // Try to make the sprite_face_test folder if it doesn't already exist. var dir = testing.tmpDir(.{}); defer dir.cleanup(); - const tmp_dir = try dir.dir.realpathAlloc(alloc, "."); + const tmp_dir = try dir.dir.realPathFileAlloc(testing.io, ".", alloc); defer alloc.free(tmp_dir); // We set this to true if we have any fails so we can @@ -481,7 +483,7 @@ fn testDrawRanges( .{ tmp_dir, i, i + 0xFF, width, height, thickness }, ); defer alloc.free(path); - try z2d.png_exporter.writeToPNGFile(atlas, path, .{}); + try z2d.png_exporter.writeToPNGFile(testing.io, atlas, path, .{}); if (try testDiffAtlas( alloc, @@ -526,7 +528,7 @@ fn testDrawRanges( .{ tmp_dir, i, i + 0xFF, width, height, thickness }, ); defer alloc.free(path); - try z2d.png_exporter.writeToPNGFile(atlas, path, .{}); + try z2d.png_exporter.writeToPNGFile(std.testing.io, atlas, path, .{}); if (try testDiffAtlas( alloc, &atlas, diff --git a/vendor/libghostty-vt/src/font/sprite/canvas.zig b/vendor/libghostty-vt/src/font/sprite/canvas.zig index 7904f20a..da54ac27 100644 --- a/vendor/libghostty-vt/src/font/sprite/canvas.zig +++ b/vendor/libghostty-vt/src/font/sprite/canvas.zig @@ -5,6 +5,7 @@ const assert = @import("../../quirks.zig").inlineAssert; const Allocator = std.mem.Allocator; const z2d = @import("z2d"); const font = @import("../main.zig"); +const global = @import("../../global.zig"); pub fn Point(comptime T: type) type { return struct { @@ -255,7 +256,7 @@ pub const Canvas = struct { /// Acquires a z2d drawing context, caller MUST deinit context. pub fn getContext(self: *Canvas) z2d.Context { - var ctx = z2d.Context.init(self.alloc, &self.sfc); + var ctx = z2d.Context.init(global.io(), self.alloc, &self.sfc); // Offset by our padding to keep // coordinates relative to the cell. ctx.setTransformation(self.transformation()); @@ -374,85 +375,17 @@ pub const Canvas = struct { ); } - /// Do an inner stroke on a z2d path, right now this involves a pretty - /// heavy workaround that uses two extra surfaces; in the future, z2d - /// should add inner and outer strokes natively. + /// Do an inner stroke using a 1/2 stroke width inset of the supplied + /// `path`. pub fn innerStrokePath( self: *Canvas, path: z2d.Path, opts: z2d.painter.StrokeOptions, color: Color, - ) (z2d.painter.StrokeError || z2d.painter.FillError)!void { - // On one surface we fill the shape, this will be a mask we - // multiply with the double-width stroke so that only the - // part inside is used. - var fill_sfc: z2d.Surface = try .init( - .image_surface_alpha8, - self.alloc, - self.sfc.getWidth(), - self.sfc.getHeight(), - ); - defer fill_sfc.deinit(self.alloc); - - // On the other we'll do the double width stroke. - var stroke_sfc: z2d.Surface = try .init( - .image_surface_alpha8, - self.alloc, - self.sfc.getWidth(), - self.sfc.getHeight(), - ); - defer stroke_sfc.deinit(self.alloc); - - // Make a closed version of the path for our fill, so - // that we can support open paths for inner stroke. - var closed_path = path; - closed_path.nodes = try path.nodes.clone(self.alloc); - defer closed_path.deinit(self.alloc); - try closed_path.close(self.alloc); - - // Fill the shape in white to the fill surface, we use - // white because this is a mask that we'll multiply with - // the stroke, we want everything inside to be the stroke - // color. - try z2d.painter.fill( - self.alloc, - &fill_sfc, - &.{ .opaque_pattern = .{ - .pixel = .{ .alpha8 = .{ .a = 255 } }, - } }, - closed_path.nodes.items, - .{}, - ); - - // Stroke the shape with double the desired width. - var mut_opts = opts; - mut_opts.line_width *= 2; - try z2d.painter.stroke( - self.alloc, - &stroke_sfc, - &.{ .opaque_pattern = .{ - .pixel = .{ .alpha8 = .{ .a = @intFromEnum(color) } }, - } }, - path.nodes.items, - mut_opts, - ); - - // We multiply the stroke sfc on to the fill surface. - // The z2d composite operation doesn't seem to work for - // this with alpha8 surfaces, so we have to do it manually. - for ( - std.mem.sliceAsBytes(fill_sfc.image_surface_alpha8.buf), - std.mem.sliceAsBytes(stroke_sfc.image_surface_alpha8.buf), - ) |*d, s| { - d.* = @intFromFloat(@round( - 255.0 * - (@as(f64, @floatFromInt(s)) / 255.0) * - (@as(f64, @floatFromInt(d.*)) / 255.0), - )); - } - - // Then we composite the result on to the main surface. - self.sfc.composite(&fill_sfc, .src_over, 0, 0, .{}); + ) (z2d.Path.OffsetError || z2d.painter.StrokeError)!void { + var inset_path = try path.offset(self.alloc, -opts.line_width / 2.0); + defer inset_path.deinit(self.alloc); + return self.strokePath(inset_path, opts, color); } /// Fill a z2d path. diff --git a/vendor/libghostty-vt/src/font/sprite/draw/powerline.zig b/vendor/libghostty-vt/src/font/sprite/draw/powerline.zig index 8658d855..35445cdd 100644 --- a/vendor/libghostty-vt/src/font/sprite/draw/powerline.zig +++ b/vendor/libghostty-vt/src/font/sprite/draw/powerline.zig @@ -292,8 +292,13 @@ pub fn drawE0B5( const radius: f64 = @min(float_width, float_height / 2); - var path = canvas.staticPath(4); + // Note on the first/final lineTo moves: this is to enforce horizontal + // lines on both ends. This ensures that when the path is offset and + // stroked, the ends are butt-capped entirely perpendicularly; curveTo + // lerping would produce very slight slopes otherwise. + var path = canvas.staticPath(6); path.moveTo(0, 0); + path.lineTo(1, 0); path.curveTo( radius * c, 0, @@ -308,9 +313,10 @@ pub fn drawE0B5( float_height - radius + radius * c, radius * c, float_height, - 0, + 1, float_height, ); + path.lineTo(0, float_height); try canvas.innerStrokePath(path.wrapped_path, .{ .line_width = @floatFromInt(metrics.box_thickness), diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png Binary files differindex e04e7726..00ee592b 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png Binary files differindex ce1b1c42..7b37e566 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png Binary files differindex 1c21f688..6b19f819 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png Binary files differindex 459822e6..04c9ac6a 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png Binary files differindex 8d7de36a..898264f3 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png Binary files differindex ab6bec96..59da98ba 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png Binary files differindex 43035aef..e3dff1f3 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png Binary files differindex fc111e2d..6069996b 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CD00...U+1CDFF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png Binary files differindex b3cda82d..3cbf0934 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png Binary files differindex e076da7c..9f62fc4b 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png Binary files differindex 366be386..ded30f0f 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png Binary files differindex 5cd7a4ef..260e536d 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png Binary files differindex 023396fe..6f408b37 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png Binary files differindex 1a4dcb01..99da9148 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png Binary files differindex 73ead485..e2136d7d 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png Binary files differindex b58ded46..2db1f743 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+1FB00...U+1FBFF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png Binary files differindex 9ae5b45f..eb0032eb 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png Binary files differindex 89011df4..99032356 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png Binary files differindex 05ff2a5f..9d6fc664 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png Binary files differindex cf96b7d1..5c6ef4db 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png Binary files differindex 804e3017..f171dd24 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png Binary files differindex aa3e2f99..4ccdfc4a 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png Binary files differindex 49f431a4..47e70eee 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png Binary files differindex 4308c536..5f0928e2 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+2800...U+28FF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png Binary files differindex addd1f49..905adfdc 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png Binary files differindex 70ae8102..c19a7bc0 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png Binary files differindex 32a42097..052ee7e7 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png Binary files differindex 46151697..159c7721 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+E000...U+E0FF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png Binary files differindex 10385379..94deceb4 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png Binary files differindex b1bb803a..59323c67 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png Binary files differindex e7415b7a..05a124ec 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png Binary files differindex 730a4c42..dcb16b3d 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F500...U+F5FF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png Binary files differindex b216174e..de2772e7 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-11x21+2.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png Binary files differindex 2243248b..8cf94a51 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-12x24+3.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png Binary files differindex f3a887d2..9e93a93a 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-18x36+4.png diff --git a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png Binary files differindex 82ce8246..7bfd9db2 100644 --- a/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png +++ b/vendor/libghostty-vt/src/font/sprite/testdata/U+F600...U+F6FF-9x17+1.png diff --git a/vendor/libghostty-vt/src/font/testdata/glyf_rasterize.png b/vendor/libghostty-vt/src/font/testdata/glyf_rasterize.png Binary files differindex f5f8fb81..689e3e91 100644 --- a/vendor/libghostty-vt/src/font/testdata/glyf_rasterize.png +++ b/vendor/libghostty-vt/src/font/testdata/glyf_rasterize.png |
