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.
81 lines
2.8 KiB
C
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;
|
|
}
|