mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
getting closer to dumb font rendering
This commit is contained in:
@ -15,10 +15,15 @@ pub fn build(b: *std.build.Builder) !void {
|
||||
exe.addPackagePath("glfw", "vendor/mach/glfw/src/main.zig");
|
||||
glfw.link(b, exe, .{});
|
||||
|
||||
exe.linkSystemLibrary("epoxy");
|
||||
|
||||
const ftlib = try ft.create(b, target, mode, .{});
|
||||
ftlib.link(exe);
|
||||
|
||||
exe.linkSystemLibrary("epoxy");
|
||||
// to link to system:
|
||||
// exe.linkSystemLibrary("freetype2");
|
||||
// exe.linkSystemLibrary("libpng");
|
||||
// exe.linkSystemLibrary("bzip2");
|
||||
// ftlib.addIncludeDirs(exe);
|
||||
|
||||
// stb if we need it
|
||||
// exe.addIncludeDir("vendor/stb");
|
||||
|
BIN
fonts/Inconsolata-Regular.ttf
Executable file
BIN
fonts/Inconsolata-Regular.ttf
Executable file
Binary file not shown.
BIN
fonts/Inconsolata.ttf
Executable file
BIN
fonts/Inconsolata.ttf
Executable file
Binary file not shown.
@ -8,8 +8,11 @@
|
||||
, vttest
|
||||
, zig
|
||||
|
||||
, bzip2
|
||||
, fontconfig
|
||||
, freetype
|
||||
, libepoxy
|
||||
, libpng
|
||||
, libGL
|
||||
, libX11
|
||||
, libXcursor
|
||||
@ -33,10 +36,14 @@
|
||||
buildInputs = [
|
||||
# TODO: non-linux
|
||||
] ++ lib.optionals stdenv.isLinux [
|
||||
fontconfig
|
||||
libepoxy
|
||||
libGL
|
||||
|
||||
fontconfig
|
||||
freetype
|
||||
libpng
|
||||
bzip2
|
||||
|
||||
libX11
|
||||
libXcursor
|
||||
libXext
|
||||
|
9
shaders/text.f.glsl
Normal file
9
shaders/text.f.glsl
Normal file
@ -0,0 +1,9 @@
|
||||
#version 120
|
||||
|
||||
varying vec2 texcoord;
|
||||
uniform sampler2D tex;
|
||||
uniform vec4 color;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = vec4(1, 1, 1, texture2D(tex, texcoord).r) * color;
|
||||
}
|
9
shaders/text.v.glsl
Normal file
9
shaders/text.v.glsl
Normal file
@ -0,0 +1,9 @@
|
||||
#version 120
|
||||
|
||||
attribute vec4 coord;
|
||||
varying vec2 texcoord;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = vec4(coord.xy, 0, 1);
|
||||
texcoord = coord.zw;
|
||||
}
|
13
src/App.zig
13
src/App.zig
@ -4,8 +4,9 @@
|
||||
const App = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const gl = @import("opengl.zig");
|
||||
const glfw = @import("glfw");
|
||||
const gl = @import("opengl.zig");
|
||||
const TextRenderer = @import("TextRenderer.zig");
|
||||
|
||||
const log = std.log;
|
||||
|
||||
@ -17,7 +18,11 @@ vao: gl.VertexArray,
|
||||
/// Initialize the main app instance. This creates the main window, sets
|
||||
/// up the renderer state, compiles the shaders, etc. This is the primary
|
||||
/// "startup" logic.
|
||||
pub fn init() !App {
|
||||
pub fn init(alloc: std.mem.Allocator) !App {
|
||||
// Setup our text renderer
|
||||
var texter = try TextRenderer.init(alloc);
|
||||
defer texter.deinit();
|
||||
|
||||
// Create our window
|
||||
const window = try glfw.Window.create(640, 480, "ghostty", null, null, .{
|
||||
.context_version_major = 3,
|
||||
@ -39,6 +44,10 @@ pub fn init() !App {
|
||||
}
|
||||
}).callback);
|
||||
|
||||
// Blending for text
|
||||
gl.c.glEnable(gl.c.GL_BLEND);
|
||||
gl.c.glBlendFunc(gl.c.GL_SRC_ALPHA, gl.c.GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Compile our shaders
|
||||
const vs = try gl.Shader.create(gl.c.GL_VERTEX_SHADER);
|
||||
try vs.setSourceAndCompile(vs_source);
|
||||
|
116
src/TextRenderer.zig
Normal file
116
src/TextRenderer.zig
Normal file
@ -0,0 +1,116 @@
|
||||
const TextRenderer = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const ftc = @import("freetype/c.zig");
|
||||
const gl = @import("opengl.zig");
|
||||
|
||||
alloc: std.mem.Allocator,
|
||||
ft: ftc.FT_Library,
|
||||
face: ftc.FT_Face,
|
||||
chars: CharList,
|
||||
|
||||
const CharList = std.ArrayListUnmanaged(Char);
|
||||
const Char = struct {
|
||||
tex: gl.Texture,
|
||||
size: @Vector(2, c_uint),
|
||||
bearing: @Vector(2, c_int),
|
||||
advance: c_uint,
|
||||
};
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) !TextRenderer {
|
||||
var ft: ftc.FT_Library = undefined;
|
||||
if (ftc.FT_Init_FreeType(&ft) != 0) {
|
||||
return error.FreetypeInitFailed;
|
||||
}
|
||||
|
||||
var face: ftc.FT_Face = undefined;
|
||||
if (ftc.FT_New_Memory_Face(
|
||||
ft,
|
||||
face_ttf,
|
||||
face_ttf.len,
|
||||
0,
|
||||
&face,
|
||||
) != 0) {
|
||||
return error.FreetypeFaceFailed;
|
||||
}
|
||||
|
||||
_ = ftc.FT_Set_Pixel_Sizes(face, 0, 48);
|
||||
|
||||
// disable byte-alignment restriction
|
||||
gl.c.glPixelStorei(gl.c.GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
// Pre-render all the ASCII characters
|
||||
var chars = try CharList.initCapacity(alloc, 128);
|
||||
var i: usize = 0;
|
||||
while (i < chars.capacity) : (i += 1) {
|
||||
// Load the character
|
||||
if (ftc.FT_Load_Char(face, i, ftc.FT_LOAD_RENDER) != 0) {
|
||||
return error.GlyphLoadFailed;
|
||||
}
|
||||
|
||||
if (face.*.glyph.*.bitmap.buffer == null) {
|
||||
// Unrenderable characters
|
||||
chars.appendAssumeCapacity(.{
|
||||
.tex = undefined,
|
||||
.size = undefined,
|
||||
.bearing = undefined,
|
||||
.advance = undefined,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate the texture
|
||||
const tex = try gl.Texture.create();
|
||||
var binding = try tex.bind(gl.c.GL_TEXTURE_2D);
|
||||
defer binding.unbind();
|
||||
try binding.image2D(
|
||||
0,
|
||||
gl.c.GL_RED,
|
||||
@intCast(c_int, face.*.glyph.*.bitmap.width),
|
||||
@intCast(c_int, face.*.glyph.*.bitmap.rows),
|
||||
0,
|
||||
gl.c.GL_RED,
|
||||
gl.c.GL_UNSIGNED_BYTE,
|
||||
face.*.glyph.*.bitmap.buffer,
|
||||
);
|
||||
try binding.parameter(gl.c.GL_TEXTURE_WRAP_S, gl.c.GL_CLAMP_TO_EDGE);
|
||||
try binding.parameter(gl.c.GL_TEXTURE_WRAP_T, gl.c.GL_CLAMP_TO_EDGE);
|
||||
try binding.parameter(gl.c.GL_TEXTURE_MIN_FILTER, gl.c.GL_LINEAR);
|
||||
try binding.parameter(gl.c.GL_TEXTURE_MAG_FILTER, gl.c.GL_LINEAR);
|
||||
|
||||
// Store the character
|
||||
chars.appendAssumeCapacity(.{
|
||||
.tex = tex,
|
||||
.size = .{
|
||||
face.*.glyph.*.bitmap.width,
|
||||
face.*.glyph.*.bitmap.rows,
|
||||
},
|
||||
.bearing = .{
|
||||
face.*.glyph.*.bitmap_left,
|
||||
face.*.glyph.*.bitmap_top,
|
||||
},
|
||||
.advance = @intCast(c_uint, face.*.glyph.*.advance.x),
|
||||
});
|
||||
}
|
||||
|
||||
return TextRenderer{
|
||||
.alloc = alloc,
|
||||
.ft = ft,
|
||||
.face = face,
|
||||
.chars = chars,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TextRenderer) void {
|
||||
// TODO: delete textures
|
||||
self.chars.deinit(self.alloc);
|
||||
|
||||
if (ftc.FT_Done_Face(self.face) != 0)
|
||||
std.log.err("freetype face deinitialization failed", .{});
|
||||
if (ftc.FT_Done_FreeType(self.ft) != 0)
|
||||
std.log.err("freetype library deinitialization failed", .{});
|
||||
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
const face_ttf = @embedFile("../fonts/Inconsolata-Regular.ttf");
|
@ -7,6 +7,7 @@ pub const Library = struct {
|
||||
/// statically link this library into the given step
|
||||
pub fn link(self: Library, other: *std.build.LibExeObjStep) void {
|
||||
self.addIncludeDirs(other);
|
||||
other.addIncludeDir(include_dir);
|
||||
other.linkLibrary(self.step);
|
||||
}
|
||||
|
||||
@ -15,7 +16,6 @@ pub const Library = struct {
|
||||
/// library.
|
||||
pub fn addIncludeDirs(self: Library, other: *std.build.LibExeObjStep) void {
|
||||
_ = self;
|
||||
other.addIncludeDir(include_dir);
|
||||
|
||||
// We need to add this directory to the include path for the final
|
||||
// app so that we can access "freetype-zig.h".
|
||||
|
@ -1,18 +1,19 @@
|
||||
const std = @import("std");
|
||||
const glfw = @import("glfw");
|
||||
const gl = @import("opengl.zig");
|
||||
const stb = @import("stb.zig");
|
||||
const fonts = @import("fonts.zig");
|
||||
|
||||
const App = @import("App.zig");
|
||||
|
||||
pub fn main() !void {
|
||||
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const gpa = general_purpose_allocator.allocator();
|
||||
defer _ = general_purpose_allocator.deinit();
|
||||
|
||||
// List our fonts
|
||||
try glfw.init(.{});
|
||||
defer glfw.terminate();
|
||||
|
||||
// Run our app
|
||||
var app = try App.init();
|
||||
var app = try App.init(gpa);
|
||||
defer app.deinit();
|
||||
try app.run();
|
||||
}
|
||||
|
Reference in New Issue
Block a user