Files
LithosAnanake/include/starkernel/ttf.h
T
Robert Allan JamesandClaude Sonnet 5 095860251a ttf.c: rasterization -- Bezier flattening + even-odd scanline fill
Punch list §25 item 4.3.7c complete. ttf_rasterize_glyph() flattens
quadratic-Bezier contours (fixed 8-segment subdivision, matching
CIRCLE/ELLIPSE's fixed-segment precedent) and fills them into a
caller-supplied bitmap via even-odd scanline fill, no AA. A local
signed Q48.16 multiply (q48_smul) handles negative outline coordinates,
since the shared q48_mul/q48_div are unsigned-only.

Verified two ways: tools/ttftest.c's ASCII-art dump + structural checks
for 'A'/'.'/'a', all recognizable and passing; and a live amd64
screendump via a throwaway TTF-PROBE word (loaded the font capsule,
rasterized 'A', blit via fb_put_pixel), showing a clearly legible 'A'
on the CANVAS -- probe reverted immediately after capture, only the
permanent ttf.c/ttf.h rasterizer remains. Compile-checked clean on all
three architectures (hal/*.c wildcard); this item's own acceptance is
the amd64 screendump, not a three-arch boot (that's 4.3.7f).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 22:24:47 -04:00

252 lines
9.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* ttf.h - TrueType font parser core (Freestanding)
*
* FABRIC.md item 4.3.7. Reads a TTF's sfnt directory plus head/maxp/loca/
* glyf/cmap tables, resolving a Unicode codepoint to a glyph index and its
* outline header (contour count, bounding box). Does NOT extract outline
* points or rasterize — that is 4.3.7a/4.3.7c. No floating point; all
* fields read here are raw integers straight from the font's own
* big-endian on-disk format (see FABRIC.md §27.7 decision #2 for why the
* Q48.16-vs-float call was made, and why it doesn't bind this file, which
* never scales anything).
*
* cmap: only format 4 (Windows/Unicode BMP) subtables are resolved. This
* covers ASCII and all of the BMP, which is what the v1 glyph repertoire
* (§27.6.4) needs. Format 12 (supplementary planes) is deferred — no
* v1 glyph requires it.
*/
#ifndef STARKERNEL_TTF_H
#define STARKERNEL_TTF_H
#include <stdint.h>
#include <stddef.h>
#include "q48_16.h"
#ifdef __cplusplus
extern "C" {
#endif
#define TTF_OK 0
#define TTF_ERR_BAD_SFNT -1
#define TTF_ERR_TABLE_MISSING -2
#define TTF_ERR_BAD_TABLE -3
#define TTF_ERR_BAD_GLYPH_INDEX -4
#define TTF_ERR_OUT_OF_BOUNDS -5
#define TTF_ERR_TOO_MANY_POINTS -6
#define TTF_ERR_TOO_MANY_CONTOURS -7
#define TTF_ERR_TOO_DEEP -8 /* composite glyph nesting exceeded TTF_MAX_COMPOSITE_DEPTH */
#define TTF_ERR_UNSUPPORTED -9 /* e.g. a non-identity composite transform or point-matched
* component args — see ttf.c's file header comment; not a
* malformed font, just a code path this parser doesn't
* implement yet */
/* Recursion guard for nested composite glyphs (a component referencing a
* component). The TTF spec doesn't hard-cap this; this is a defensive
* limit for freestanding/kernel-stack safety. */
#define TTF_MAX_COMPOSITE_DEPTH 8
/** Glyph index returned for "no mapping" by ttf_codepoint_to_glyph(). */
#define TTF_GLYPH_MISSING 0
/**
* Parsed font handle. Borrows the caller's buffer (does not copy or own
* it) — the buffer must outlive the ttf_font_t.
*/
typedef struct {
const uint8_t *data;
uint32_t size;
uint32_t head_off;
uint32_t maxp_off;
uint32_t loca_off;
uint32_t loca_len;
uint32_t glyf_off;
uint32_t glyf_len;
uint16_t units_per_em;
int16_t index_to_loc_format; /* 0 = Offset16 (x2), 1 = Offset32 */
uint16_t num_glyphs;
/* Selected cmap subtable (format 4 only, see file header comment). */
uint32_t cmap_subtable_off;
int has_cmap;
} ttf_font_t;
/** Raw glyf record header, per §27.6/4.3.7's "done when" clause. */
typedef struct {
int16_t num_contours; /* >= 0 simple glyph, < 0 composite glyph */
int16_t x_min;
int16_t y_min;
int16_t x_max;
int16_t y_max;
uint32_t glyf_offset; /* absolute file offset of this glyph's record */
uint32_t glyf_length; /* bytes; 0 for an empty glyph (e.g. space) */
} ttf_glyph_header_t;
/**
* ttf_parse - Locate and validate the sfnt directory and the head/maxp/
* loca/glyf tables (cmap is optional; ttf_codepoint_to_glyph() fails
* cleanly if absent). Does not copy `data` — `out` borrows it.
*
* @param data Whole .ttf file contents
* @param size Length of data in bytes
* @param out Parsed handle to populate
* @return TTF_OK, or a TTF_ERR_* code
*/
int ttf_parse(const uint8_t *data, uint32_t size, ttf_font_t *out);
/**
* ttf_codepoint_to_glyph - Resolve a Unicode codepoint via the font's
* format-4 cmap subtable.
*
* @return glyph index, or TTF_GLYPH_MISSING if unmapped or no cmap
*/
uint32_t ttf_codepoint_to_glyph(const ttf_font_t *font, uint32_t codepoint);
/**
* ttf_glyph_header - Read a glyph's outline header (contour count,
* bounding box) via loca + glyf. Does not extract contour points.
*
* @param glyph_index As returned by ttf_codepoint_to_glyph()
* @param out Header to populate
* @return TTF_OK, or a TTF_ERR_* code
*/
int ttf_glyph_header(const ttf_font_t *font, uint32_t glyph_index,
ttf_glyph_header_t *out);
/** One outline point, in raw font design units (NOT scaled by unitsPerEm —
* that's the caller's job, same convention as ttf_glyph_header_t's bbox),
* expressed in Q48.16. Two's-complement negative values are expected and
* correct for q48_add/q48_sub and for q48_from_u64-style left-shift
* conversion; this module never calls q48_mul/q48_div on outline
* coordinates (see ttf.c's file header comment for why). */
typedef struct {
q48_16_t x, y;
uint8_t on_curve;
} ttf_point_t;
/** Simple- and composite-glyph outline, flattened to one point list plus
* per-contour end indices (TrueType convention: contour_ends[c] is the
* index of the LAST point of contour c, inclusive; points are shared
* across contours only in the sense that contour c+1 starts right after
* contour_ends[c]). Caller supplies both backing arrays — this module
* never allocates. */
typedef struct {
ttf_point_t *points;
uint32_t max_points;
uint32_t point_count;
uint16_t *contour_ends;
uint32_t max_contours;
uint32_t contour_count;
} ttf_outline_t;
/**
* ttf_glyph_outline - Extract a glyph's outline (simple or composite,
* recursively resolving composite components) into caller-supplied
* buffers.
*
* Composite components with a non-identity transform (any scale/rotation/
* skew, i.e. anything but a pure (dx,dy) translation) or with
* point-matched (rather than xy-offset) placement args return
* TTF_ERR_UNSUPPORTED rather than silently producing a wrong outline —
* see ttf.c's file header comment for why, and check that limitation
* before relying on this for an arbitrary font.
*
* @return TTF_OK, or a TTF_ERR_* code (including TTF_ERR_TOO_MANY_POINTS/
* _CONTOURS if a caller buffer is too small, and TTF_ERR_TOO_DEEP
* if composite nesting exceeds TTF_MAX_COMPOSITE_DEPTH)
*/
int ttf_glyph_outline(const ttf_font_t *font, uint32_t glyph_index, ttf_outline_t *out);
/** A caller-owned 8-bit-per-pixel bitmap. `ttf_rasterize_glyph()` writes
* `fill_value` into covered pixels and leaves everything else untouched
* (it does not clear the buffer first — caller's job, so repeated
* rasterization into the same bitmap, e.g. for a text run, composites
* correctly without an extra clear between glyphs). */
typedef struct {
uint8_t *pixels;
uint32_t width;
uint32_t height;
} ttf_bitmap_t;
/**
* ttf_rasterize_glyph - Flatten a glyph's outline (quadratic Bezier
* contours, fixed segment count per curve — see ttf.c's file header
* comment) and fill it into `out` using the even-odd rule (FABRIC.md item
* 4.3.7c; see that item's completion note for why even-odd rather than
* nonzero winding — correct for the v1 glyph repertoire's non-self-
* intersecting nested contours, not necessarily for an arbitrary font).
* No antialiasing (explicitly deferred, per 4.3.7c's own "done when"
* clause). Never allocates — flattening uses a fixed-size local buffer
* bounded by TTF_RASTER_MAX_POINTS/TTF_RASTER_MAX_CONTOURS.
*
* @param scale Font-design-units-to-pixels scale, Q48.16, e.g.
* q48_div(q48_from_u64(size_px), q48_from_u64(font->units_per_em))
* @param origin_x Pixel-space X (Q48.16) of the glyph's (0,0) font origin
* within `out`
* @param origin_y Pixel-space Y (Q48.16) of the glyph's baseline (font
* y=0) within `out`; font Y-up is flipped to raster
* Y-down internally
* @return TTF_OK, or a TTF_ERR_* code (including TTF_ERR_TOO_MANY_POINTS/
* _CONTOURS if the flattened outline exceeds the local buffer)
*/
int ttf_rasterize_glyph(const ttf_font_t *font, uint32_t glyph_index,
q48_16_t scale, q48_16_t origin_x, q48_16_t origin_y,
uint8_t fill_value, ttf_bitmap_t *out);
#ifdef __STARKERNEL__
#include "capsule.h"
/**
* ttf_load_from_capsule - Resolve a font capsule by name and parse it,
* zero-copy (FABRIC.md item 4.3.7b). `out` borrows the capsule payload
* directly from `arena` — no kmalloc, no decode step, since the capsule
* is already a raw-byte match of the source `.ttf` (see
* capsules/fonts/README.md and FABRIC.md §27.7's 2026-08-10 correction:
* capsule storage needs no hex/base64 text-encoding, `tools/mkcapsule.c`
* already embeds arbitrary files as raw bytes). Validates the capsule's
* content hash (`capsule_validate(..., verify_hash=1)`) before parsing.
*
* @param capsule_name Colon-separated capsule name, e.g.
* "fonts:JetBrainsMono-Regular.ttf"
* @return TTF_OK, TTF_ERR_TABLE_MISSING if the capsule isn't found, or a
* capsule-validation/ttf_parse TTF_ERR_* code
*/
int ttf_load_from_capsule(
const CapsuleDirHeader *dir,
const CapsuleDesc *descs,
const CapsuleNameEntry *names,
const uint8_t *arena,
const char *capsule_name,
ttf_font_t *out);
#endif /* __STARKERNEL__ */
#ifdef __cplusplus
}
#endif
#endif /* STARKERNEL_TTF_H */