getting closer to dumb font rendering

This commit is contained in:
Mitchell Hashimoto
2022-04-03 22:04:42 -07:00
parent 5d1d153955
commit 670af17a1b
10 changed files with 166 additions and 10 deletions

View File

@ -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

Binary file not shown.

BIN
fonts/Inconsolata.ttf Executable file

Binary file not shown.

View File

@ -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
View 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
View 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;
}

View File

@ -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
View 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");

View File

@ -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".

View File

@ -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();
}