commit 5c35547

uint  ·  2026-05-04 14:46:56 +0000 UTC
parent 5e9b0d5
add font rendering
4 files changed,  +212, -4
+2, -1
 1@@ -1,10 +1,11 @@
 2 CC = cc
 3 CFLAGS = -std=c99 -Wall -Wextra
 4 CPPFLAGS = -Isource/external -Isource/include
 5-LDFLAGS = -framework Cocoa -framework CoreVideo -framework IOKit -framework CoreGraphics -framework CoreFoundation -framework Carbon
 6+LDFLAGS = -lschrift -lgrapheme -framework Cocoa -framework CoreVideo -framework IOKit -framework CoreGraphics -framework CoreFoundation -framework Carbon
 7 
 8 SRCS = source/cterm.c \
 9        source/util.c  \
10+       source/font.c  \
11        source/draw.c
12 OUT = cterm
13 
+26, -3
 1@@ -7,13 +7,15 @@
 2 #include <schrift.h>
 3 
 4 #include "draw.h"
 5+#include "font.h"
 6 #include "util.h"
 7 
 8 #define WIDTH  800
 9 #define HEIGHT 600
10-#define CELLW  9
11-#define CELLH  16
12+#define CELLW  18
13+#define CELLH  32
14 
15+Font font;
16 RGFW_window* win = NULL;
17 RGFW_surface* surf = NULL;
18 uint32_t* pixels = NULL;
19@@ -27,6 +29,8 @@ static void run(void);
20  */
21 static void cleanup(void)
22 {
23+	font_free(&font);
24+
25 	if (surf)
26 		RGFW_surface_free(surf);
27 	if (pixels)
28@@ -40,6 +44,7 @@ static void cleanup(void)
29  */
30 static void init(void)
31 {
32+	/* window */
33 	if (!(win = RGFW_createWindow("cterm", 0, 0, 800, 600, 0)))
34 		die(1, "failed to create window");
35 
36@@ -52,6 +57,11 @@ static void init(void)
37 	);
38 	if (!surf)
39 		die(1, "failed to create window surface");
40+
41+	/* font */
42+	if (font_load(&font, "/System/Library/Fonts/Supplemental/Andale Mono.ttf", 24.0) < 0)
43+		die(1, "failed to load font");
44+	font.aa = false;
45 }
46 
47 /**
48@@ -65,13 +75,26 @@ static void run(void)
49 		while (RGFW_window_checkEvent(win, &ev))
50 			;
51 
52-		draw_clear(pixels, WIDTH, HEIGHT, rgba(255, 0, 0, 255)); /* TODO: opacity */
53+		draw_clear(pixels, WIDTH, HEIGHT, rgba(0, 0, 0, 255)); /* TODO: opacity */
54 
55 		draw_cell(pixels, WIDTH, HEIGHT, 0, 0, CELLW, CELLH, rgba(255, 0, 0, 255));
56 		draw_cell(pixels, WIDTH, HEIGHT, 1, 0, CELLW, CELLH, rgba(0, 255, 0, 255));
57 		draw_cell(pixels, WIDTH, HEIGHT, 2, 0, CELLW, CELLH, rgba(0, 0, 255, 255));
58 		draw_cursor(pixels, WIDTH, HEIGHT, 3, 0, CELLW, CELLH, rgba(255, 255, 255, 255));
59 
60+		/* text */
61+		font_draw_codepoint(&font, pixels, WIDTH, HEIGHT,
62+				0 * CELLW, (int)font.asc, 'r', rgba(0, 0, 0, 255));
63+
64+		font_draw_codepoint(&font, pixels, WIDTH, HEIGHT,
65+				1 * CELLW, (int)font.asc, 'g', rgba(0, 0, 0, 255));
66+
67+		font_draw_codepoint(&font, pixels, WIDTH, HEIGHT,
68+				2 * CELLW, (int)font.asc, 'b', rgba(0, 0, 0, 255));
69+
70+		font_draw_codepoint(&font, pixels, WIDTH, HEIGHT,
71+				3 * CELLW, (int)font.asc, 'c', rgba(0, 0, 0, 255));
72+
73 		RGFW_window_blitSurface(win, surf);
74 	}
75 }
+135, -0
  1@@ -0,0 +1,135 @@
  2+#include "font.h"
  3+#include "schrift.h"
  4+
  5+#include <stdlib.h>
  6+#include <string.h>
  7+
  8+static void blend(uint32_t* dst, int w, int h, int x, int y, uint8_t alpha,
  9+                  uint32_t fg);
 10+
 11+/**
 12+ * @brief blend a pixel using alpha compositong
 13+ *
 14+ * @param dst buffer to write to
 15+ * @param x pixel x-position
 16+ * @param y piyel y-position
 17+ * @param alpha coverage from glyph
 18+ * @param fg foreground colour
 19+ */
 20+static void blend(uint32_t* dst, int w, int h, int x, int y, uint8_t alpha,
 21+                  uint32_t fg)
 22+{
 23+	uint8_t* px;
 24+	uint8_t fg_r;
 25+	uint8_t fg_g;
 26+	uint8_t fg_b;
 27+
 28+	if (x < 0 || x >= w || y < 0 || y >= h || alpha == 0)
 29+		/* transparent/oob */
 30+		return;
 31+
 32+	/* ptr to px at (x, y) */
 33+	px = (uint8_t*)&dst[y * w + x];
 34+
 35+	/* unpack fg colour*/
 36+	fg_r = (uint8_t)(fg & 0xff);
 37+	fg_g = (uint8_t)((fg >> 8) & 0xff);
 38+	fg_b = (uint8_t)((fg >> 16) & 0xff);
 39+
 40+	/* alpha blend each channel */
 41+	px[0] = (uint8_t)((fg_r * alpha + px[0] * (255 - alpha)) / 255);
 42+	px[1] = (uint8_t)((fg_g * alpha + px[1] * (255 - alpha)) / 255);
 43+	px[2] = (uint8_t)((fg_b * alpha + px[2] * (255 - alpha)) / 255);
 44+
 45+	px[3] = 255;
 46+}
 47+
 48+int font_load(Font* f, const char* path, double size)
 49+{
 50+	SFT_LMetrics lm;
 51+
 52+	if (!f || !path || size <= 0)
 53+		return -1;
 54+	memset(f, 0, sizeof(*f));
 55+
 56+	f->font = sft_loadfile(path);
 57+	if (!f->font)
 58+		return -1;
 59+
 60+	/* configure font */
 61+	f->size = size;
 62+	f->sft.font = f->font;
 63+	f->sft.xScale = size;
 64+	f->sft.yScale = size;
 65+	f->sft.xOffset = 0;
 66+	f->sft.yOffset = 0;
 67+	f->sft.flags = SFT_DOWNWARD_Y;
 68+
 69+	/* get font line metrics */
 70+	if (sft_lmetrics(&f->sft, &lm) < 0) {
 71+		sft_freefont(f->font);
 72+		memset(f, 0, sizeof(*f));
 73+		return -1;
 74+	}
 75+	f->asc = lm.ascender;
 76+	f->dsc = lm.descender;
 77+	
 78+	return 0;
 79+}
 80+
 81+void font_free(Font* f)
 82+{
 83+	if (!f)
 84+		return;
 85+	if (f->font)
 86+		sft_freefont(f->font);
 87+	memset(f, 0, sizeof(*f));
 88+}
 89+
 90+void font_draw_codepoint(Font* f, uint32_t* dst, int w, int h, int x,
 91+                         int baseline, uint32_t cp, uint32_t fg)
 92+{
 93+	SFT_Glyph glyph;
 94+	SFT_GMetrics gm;
 95+	SFT_Image img;
 96+	uint8_t* bmp;
 97+	int bx;
 98+	int by;
 99+
100+	/* get glyph metics */
101+	if (!f || !f->font || !dst)
102+		return;
103+	if (sft_lookup(&f->sft, cp, &glyph) < 0)
104+		return;
105+	if (sft_gmetrics(&f->sft, glyph, &gm) < 0)
106+		return;
107+	if (gm.minWidth <= 0 || gm.minHeight <= 0)
108+		return;
109+
110+	bmp = calloc(gm.minWidth*gm.minHeight, 1);
111+	if (!bmp)
112+		return;
113+
114+	img.pixels = bmp;
115+	img.width = gm.minWidth;
116+	img.height = gm.minHeight;
117+
118+	if (sft_render(&f->sft, glyph, img) == 0) {
119+		/* copy bmp to dst */
120+		for (by = 0; by < gm.minHeight; by++) {
121+			for (bx = 0; bx < gm.minWidth; bx++) {
122+				/* TODO: proper aa controls */
123+				uint8_t a = bmp[(by*gm.minWidth) + bx]; /* alpha */
124+				uint8_t aa = f->aa ? a : (a > 128 ? 255 : 0);
125+
126+				/* translate bpm pixels to dst pixels */
127+				int dst_x = x + (int)gm.leftSideBearing + bx;
128+				int dst_y = baseline + gm.yOffset + by;
129+
130+				blend(dst, w, h, dst_x, dst_y, aa, fg);
131+			}
132+		}
133+	}
134+	free(bmp);
135+}
136+
+49, -0
 1@@ -0,0 +1,49 @@
 2+#ifndef FONT_H
 3+#define FONT_H
 4+
 5+#include <stdbool.h>
 6+
 7+#include <schrift.h>
 8+
 9+typedef struct {
10+	SFT_Font*     font;
11+	SFT           sft;
12+	double        size;
13+	double        asc; /* ascent */
14+	double        dsc; /* descent */
15+	bool          aa;  /* anti-aliasing */
16+} Font;
17+
18+/**
19+ * @brief load font from path
20+ *
21+ * @param f font struct to write info to
22+ * @param path font path
23+ * @param size size of font
24+ */
25+int font_load(Font* f, const char* path, double size);
26+
27+/**
28+ * @brief free a loaded font
29+ *
30+ * @param f font struct to free
31+ */
32+void font_free(Font* f);
33+
34+/**
35+ * @brief draw one codepoint to buffer
36+ *
37+ * @param f font to draw from
38+ * @param dst destination buffer
39+ * @param w buffer width
40+ * @param h buffer height
41+ * @param x x-position to draw at
42+ * @param baseline text baseline y-position
43+ * @param cp (unicode) codepoint to draw
44+ * @param fg foreground colour
45+ */
46+void font_draw_codepoint(Font* f, uint32_t* dst, int w, int h, int x,
47+                         int baseline, uint32_t cp, uint32_t fg);
48+
49+#endif /* FONT_H */
50+