SchOS/include/text.h
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

86 lines
3.7 KiB
C

#pragma once
// Text rendering.
//
// The console draws text by indexing `font_data[256][16]` and stepping the
// cursor eight pixels. That is a *character cell grid*, and it is the thing in
// the way -- not the font. A grid cannot do variable-width glyphs, cannot
// position text anywhere except on a multiple of eight, cannot anti-alias, and
// cannot represent a codepoint above 255. Swapping the bitmap for a nicer
// bitmap changes none of that.
//
// So this is a layer, not a font. Three things it fixes:
//
// *Glyphs have metrics.* A glyph knows its own size, its bearing and how far
// to advance afterwards. That is what variable-width text is; everything else
// follows from it.
//
// *It draws into a surface, not into the screen.* A compositor draws into an
// off-screen buffer and presents it. Rendering that writes straight to the
// VGA aperture is unusable to it, which is why this takes a target.
//
// *The font is behind an interface.* The bitmap font is one implementation.
// A TrueType rasteriser is another, and arrives without any caller changing:
// glyphs carry 8-bit coverage, so an anti-aliased outline renderer already
// has somewhere to put its output.
struct font;
// Somewhere to draw. Deliberately not a `struct device` or a framebuffer: the
// same code has to work on the virtio-gpu back buffer, on a window's own
// buffer, and on the VGA aperture, and none of those know about each other.
struct surface {
void *pixels; // 32-bit 0x00RRGGBB, the format everything here uses
uint32 stride; // bytes per row, not pixels -- they differ on real hardware
int width, height;
};
// One rendered glyph. `cov` is 8-bit coverage, not a bitmap: 0 is background,
// 255 is solid, and everything between is an anti-aliased edge. A bitmap font
// fills it with 0 and 255 only, and costs nothing for the generality.
struct glyph {
const unsigned char *cov; // w*h bytes, row-major
int w, h; // size of the coverage bitmap
int bearing_x, bearing_y; // offset from the pen to the top-left of `cov`
int advance; // how far the pen moves afterwards
};
struct font_ops {
// Fill `out` for `cp`. Returns 0, or -1 if the font has no such glyph --
// callers substitute rather than failing, because a missing glyph must not
// stop a line of text.
int (*glyph)(struct font *, uint32 cp, struct glyph *out);
int (*height)(struct font *); // line height
int (*baseline)(struct font *); // pen offset from the top of the line
};
struct font {
const struct font_ops *ops;
void *priv;
};
// The built-in bitmap font, as a font provider. Always available, needs no
// allocation, and is what the console falls back to before any filesystem
// exists to load a real typeface from.
struct font *font_builtin(void);
// Draw one UTF-8 string. Returns the x advance.
//
// `x, y` is the pen position on the *baseline*, which is how every text API
// that is not a terminal works -- glyphs of different heights then sit on a
// common line instead of being top-aligned.
int text_draw(struct surface *, struct font *, int x, int y,
const char *utf8, uint32 color);
// Width the same string would occupy, without drawing it. Needed before you
// draw, for centring and for laying out anything at all.
int text_measure(struct font *, const char *utf8);
// One glyph, for callers doing their own layout.
int text_draw_glyph(struct surface *, struct font *, int x, int y,
uint32 cp, uint32 color);
// Decode one UTF-8 sequence. Returns the codepoint and advances *p.
// Malformed input yields U+FFFD and consumes one byte, so a decoder loop
// always terminates.
uint32 utf8_next(const char **p);