SchOS/tools/ttftest.c
Nexoniarz d0f7acf9eb Initial commit: Schunix as it stands before the SchOS rename
An x86-64 operating system built from xv6, now largely rewritten: SMP,
a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF
rasteriser, and a Linux-numbered syscall ABI.

No history before this point -- the tree was never under version control.
This commit exists so the rename that follows has something to undo to.
2026-08-09 13:47:01 +02:00

81 lines
2.8 KiB
C

// Host harness for kernel/dev/ttf.c.
//
// A rasteriser is checked by looking at it. This renders a string with the real
// font and writes a PGM, which is the smallest image format that needs no
// compression -- the point is the glyphs, not the container.
//
// build/tools/ttftest <font.ttf> <px> "text" out.pgm
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TTF_HOST
#include "../kernel/dev/ttf.c"
int ttf_render(struct ttf *, uint32, struct glyph *, unsigned char *, int, int *, int *);
int main(int argc, char **argv)
{
if (argc < 5) { fprintf(stderr, "usage: ttftest font.ttf px \"text\" out.pgm\n"); return 1; }
const char *path = argv[1];
int px = atoi(argv[2]);
const char *text = argv[3];
FILE *fp = fopen(path, "rb");
if (!fp) { perror(path); return 1; }
fseek(fp, 0, SEEK_END); long n = ftell(fp); fseek(fp, 0, SEEK_SET);
unsigned char *buf = malloc(n);
if (fread(buf, 1, n, fp) != (size_t)n) { fprintf(stderr, "short read\n"); return 1; }
fclose(fp);
struct ttf f;
if (ttf_open(&f, buf, (uint32)n, px) != 0) {
fprintf(stderr, "not a TrueType font this can read\n"); return 1;
}
printf("unitsPerEm %d, %d glyphs, ascent %d descent %d, %s loca\n",
f.units_per_em, f.num_glyphs, f.ascent, f.descent,
f.long_loca ? "long" : "short");
struct font *fo = ttf_font(&f);
int H = fo->ops->height(fo) + px; // room for descenders and padding
int base = fo->ops->baseline(fo) + px / 2;
int W = 16;
for (const char *p = text; *p; p++) W += px; // generous
unsigned char *img = calloc(W * H, 1);
int pen = 8, drawn = 0, missing = 0;
for (const char *p = text; *p; p++) {
struct glyph g;
// Through the provider, not ttf_render, so the glyph cache is on the
// path being tested rather than bypassed by the harness.
if (fo->ops->glyph(fo, (unsigned char)*p, &g) != 0) {
missing++;
pen += px / 2;
continue;
}
for (int gy = 0; gy < g.h; gy++)
for (int gx = 0; gx < g.w; gx++) {
int X = pen + g.bearing_x + gx;
int Y = base - g.bearing_y + gy;
if (X < 0 || Y < 0 || X >= W || Y >= H) continue;
int v = img[Y * W + X] + g.cov[gy * g.w + gx];
img[Y * W + X] = v > 255 ? 255 : v;
}
pen += g.advance;
drawn++;
}
// White on black inverted to black on white, which is how text is read.
for (int i = 0; i < W * H; i++) img[i] = 255 - img[i];
FILE *o = fopen(argv[4], "wb");
fprintf(o, "P5\n%d %d\n255\n", W, H);
fwrite(img, 1, W * H, o);
fclose(o);
printf("%d glyphs drawn, %d missing, %d px advance, image %dx%d -> %s\n",
drawn, missing, pen - 8, W, H, argv[4]);
return drawn ? 0 : 1;
}