commit 5c35547
uint
·
2026-05-04 14:46:56 +0000 UTC
parent 5e9b0d5
add font rendering
4 files changed,
+212,
-4
M
Makefile
+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+