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.
This commit is contained in:
commit
d0f7acf9eb
275 changed files with 51417 additions and 0 deletions
242
kernel/dev/text.c
Normal file
242
kernel/dev/text.c
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
// Text rendering. See include/text.h for what this replaces and why.
|
||||
|
||||
#include "types.h"
|
||||
#include "defs.h"
|
||||
#include "param.h"
|
||||
#include "text.h"
|
||||
|
||||
// Declared, not included. include/font.h *defines* font_data rather than
|
||||
// declaring it, so including it here as well as in vga.c gives two definitions
|
||||
// of the same array and the link fails. The header is generated data; leaving
|
||||
// it alone and referring to the symbol is less fragile than editing it.
|
||||
extern unsigned char font_data[256][16];
|
||||
|
||||
// --- UTF-8 -----------------------------------------------------------------
|
||||
|
||||
uint32
|
||||
utf8_next(const char **p)
|
||||
{
|
||||
const unsigned char *s = (const unsigned char *)*p;
|
||||
uint32 c = s[0];
|
||||
|
||||
if (c < 0x80) {
|
||||
*p += 1;
|
||||
return c;
|
||||
}
|
||||
|
||||
int extra;
|
||||
uint32 cp;
|
||||
if ((c & 0xE0) == 0xC0) { extra = 1; cp = c & 0x1F; }
|
||||
else if ((c & 0xF0) == 0xE0) { extra = 2; cp = c & 0x0F; }
|
||||
else if ((c & 0xF8) == 0xF0) { extra = 3; cp = c & 0x07; }
|
||||
else {
|
||||
// A continuation byte or 0xF8..0xFF where a lead byte should be. Consume
|
||||
// exactly one so a malformed string cannot stall the loop -- a decoder that
|
||||
// does not advance on bad input is a hang, not a rendering artefact.
|
||||
*p += 1;
|
||||
return 0xFFFD;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= extra; i++) {
|
||||
if ((s[i] & 0xC0) != 0x80) { // truncated sequence
|
||||
*p += 1;
|
||||
return 0xFFFD;
|
||||
}
|
||||
cp = (cp << 6) | (s[i] & 0x3F);
|
||||
}
|
||||
*p += extra + 1;
|
||||
|
||||
// Surrogates are not characters, and an overlong encoding is the classic way
|
||||
// to smuggle a byte past a filter that checks the decoded form.
|
||||
if (cp >= 0xD800 && cp <= 0xDFFF)
|
||||
return 0xFFFD;
|
||||
if ((extra == 1 && cp < 0x80) || (extra == 2 && cp < 0x800) ||
|
||||
(extra == 3 && cp < 0x10000))
|
||||
return 0xFFFD;
|
||||
return cp;
|
||||
}
|
||||
|
||||
// --- the built-in bitmap font as a provider --------------------------------
|
||||
//
|
||||
// font_data is 256 CP437 glyphs of 8x16, one bit per pixel. Expanded here into
|
||||
// 8-bit coverage so that every caller works in one representation and an
|
||||
// outline rasteriser can be added without touching any of them.
|
||||
//
|
||||
// The expansion buffer is static and reused per glyph. That is fine because
|
||||
// glyphs are consumed immediately by the blitter and nothing holds one; it
|
||||
// would not be fine the moment a caller kept a struct glyph across calls, which
|
||||
// is why the header says nothing about the lifetime being longer than that.
|
||||
|
||||
#define BUILTIN_W 8
|
||||
#define BUILTIN_H 16
|
||||
|
||||
static unsigned char builtin_cov[BUILTIN_W * BUILTIN_H];
|
||||
|
||||
// Unicode to CP437, for the range where they differ and it matters.
|
||||
//
|
||||
// The built-in font is CP437, not Latin-1 -- a fact that has bitten this
|
||||
// codebase before. Rather than pretend otherwise, map the handful of
|
||||
// codepoints worth having and fall back for the rest. This table exists to be
|
||||
// *deleted* when a real font arrives; it is a bridge, not a design.
|
||||
static uint32
|
||||
to_cp437(uint32 cp)
|
||||
{
|
||||
if (cp < 0x80)
|
||||
return cp;
|
||||
switch (cp) {
|
||||
case 0x00A0: return 0x20; // no-break space
|
||||
case 0x00B0: return 0xF8; // degree
|
||||
case 0x00B1: return 0xF1; // plus-minus
|
||||
case 0x00B7: return 0xFA; // middle dot
|
||||
case 0x2022: return 0x07; // bullet
|
||||
case 0x2190: return 0x1B; // arrows
|
||||
case 0x2191: return 0x18;
|
||||
case 0x2192: return 0x1A;
|
||||
case 0x2193: return 0x19;
|
||||
case 0x2500: return 0xC4; // box drawing, the part worth keeping
|
||||
case 0x2502: return 0xB3;
|
||||
case 0x250C: return 0xDA;
|
||||
case 0x2510: return 0xBF;
|
||||
case 0x2514: return 0xC0;
|
||||
case 0x2518: return 0xD9;
|
||||
case 0x251C: return 0xC3;
|
||||
case 0x2524: return 0xB4;
|
||||
case 0x252C: return 0xC2;
|
||||
case 0x2534: return 0xC1;
|
||||
case 0x253C: return 0xC5;
|
||||
case 0x2588: return 0xDB; // full block
|
||||
case 0x2591: return 0xB0; // shades
|
||||
case 0x2592: return 0xB1;
|
||||
case 0x2593: return 0xB2;
|
||||
default: return 0; // "no glyph"
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
builtin_glyph(struct font *f, uint32 cp, struct glyph *out)
|
||||
{
|
||||
uint32 idx = to_cp437(cp);
|
||||
if (idx == 0 && cp != 0)
|
||||
return -1; // caller substitutes
|
||||
|
||||
for (int y = 0; y < BUILTIN_H; y++) {
|
||||
unsigned char bits = font_data[idx & 0xFF][y];
|
||||
for (int x = 0; x < BUILTIN_W; x++)
|
||||
builtin_cov[y * BUILTIN_W + x] = (bits & (0x80 >> x)) ? 0xFF : 0x00;
|
||||
}
|
||||
|
||||
out->cov = builtin_cov;
|
||||
out->w = BUILTIN_W;
|
||||
out->h = BUILTIN_H;
|
||||
out->bearing_x = 0;
|
||||
// The whole cell sits above the baseline, less two rows of descender space.
|
||||
// The value matters only for consistency with whatever font comes next.
|
||||
out->bearing_y = BUILTIN_H - 3;
|
||||
out->advance = BUILTIN_W;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_height(struct font *f) { return BUILTIN_H; }
|
||||
static int builtin_baseline(struct font *f) { return BUILTIN_H - 3; }
|
||||
|
||||
static const struct font_ops builtin_ops = {
|
||||
.glyph = builtin_glyph,
|
||||
.height = builtin_height,
|
||||
.baseline = builtin_baseline,
|
||||
};
|
||||
|
||||
static struct font builtin = { &builtin_ops, 0 };
|
||||
|
||||
struct font *
|
||||
font_builtin(void)
|
||||
{
|
||||
return &builtin;
|
||||
}
|
||||
|
||||
// --- blitting --------------------------------------------------------------
|
||||
|
||||
// Blend `color` over the pixel at (x, y) by coverage a.
|
||||
//
|
||||
// Reads the destination, which a bitmap font never needs -- but an
|
||||
// anti-aliased edge is defined as a blend with what is already there, and a
|
||||
// renderer that cannot do it is a renderer that cannot draw a real typeface.
|
||||
static void
|
||||
blend_px(struct surface *s, int x, int y, uint32 color, unsigned a)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= s->width || y >= s->height)
|
||||
return;
|
||||
|
||||
uint32 *px = (uint32 *)((unsigned char *)s->pixels + (uint64)y * s->stride) + x;
|
||||
if (a >= 255) {
|
||||
*px = color;
|
||||
return;
|
||||
}
|
||||
uint32 dst = *px;
|
||||
uint32 r = (((color >> 16) & 0xFF) * a + ((dst >> 16) & 0xFF) * (255 - a)) / 255;
|
||||
uint32 g = (((color >> 8) & 0xFF) * a + ((dst >> 8) & 0xFF) * (255 - a)) / 255;
|
||||
uint32 b = (((color ) & 0xFF) * a + ((dst ) & 0xFF) * (255 - a)) / 255;
|
||||
*px = (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
int
|
||||
text_draw_glyph(struct surface *s, struct font *f, int x, int y,
|
||||
uint32 cp, uint32 color)
|
||||
{
|
||||
struct glyph g;
|
||||
|
||||
if (f->ops->glyph(f, cp, &g) != 0) {
|
||||
// Substitute rather than skip. A missing glyph that draws nothing makes
|
||||
// text silently shorter and the fault impossible to see; a visible box
|
||||
// says exactly what happened and where.
|
||||
if (f->ops->glyph(f, 0x25A1, &g) != 0 && // white square
|
||||
f->ops->glyph(f, '?', &g) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ox = x + g.bearing_x;
|
||||
int oy = y - g.bearing_y; // bearing is measured up from the baseline
|
||||
|
||||
for (int gy = 0; gy < g.h; gy++)
|
||||
for (int gx = 0; gx < g.w; gx++) {
|
||||
unsigned a = g.cov[gy * g.w + gx];
|
||||
if (a)
|
||||
blend_px(s, ox + gx, oy + gy, color, a);
|
||||
}
|
||||
|
||||
return g.advance;
|
||||
}
|
||||
|
||||
int
|
||||
text_draw(struct surface *s, struct font *f, int x, int y,
|
||||
const char *utf8, uint32 color)
|
||||
{
|
||||
int pen = x;
|
||||
const char *p = utf8;
|
||||
|
||||
while (*p) {
|
||||
uint32 cp = utf8_next(&p);
|
||||
if (cp == '\n')
|
||||
break; // layout is the caller's business, not ours
|
||||
pen += text_draw_glyph(s, f, pen, y, cp, color);
|
||||
}
|
||||
return pen - x;
|
||||
}
|
||||
|
||||
int
|
||||
text_measure(struct font *f, const char *utf8)
|
||||
{
|
||||
int w = 0;
|
||||
const char *p = utf8;
|
||||
struct glyph g;
|
||||
|
||||
while (*p) {
|
||||
uint32 cp = utf8_next(&p);
|
||||
if (cp == '\n')
|
||||
break;
|
||||
if (f->ops->glyph(f, cp, &g) == 0)
|
||||
w += g.advance;
|
||||
else if (f->ops->glyph(f, '?', &g) == 0)
|
||||
w += g.advance;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
Loading…
Reference in a new issue