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>
This commit is contained in:
Robert Allan James
2026-08-10 22:24:47 -04:00
co-authored by Claude Sonnet 5
parent 2ef8d26c6f
commit 095860251a
5 changed files with 468 additions and 2 deletions
+76
View File
@@ -149,6 +149,78 @@ static void test_outlines(const ttf_font_t *font) {
}
}
/* Rasterization (4.3.7c) sanity check: rasterize a glyph at a modest
* pixel size, print an ASCII-art dump for visual "is this recognizable"
* inspection (this item's own acceptance criterion), plus loose
* structural checks -- fill coverage in a plausible range, background
* untouched in the far corner -- since there's no independent reference
* rasterizer to diff against pixel-for-pixel (unlike 4.3.7/4.3.7a, which
* had a from-scratch Python decoder to check against). */
static void test_rasterize(const ttf_font_t *font, uint32_t codepoint, const char *label) {
enum { SIZE_PX = 28, MARGIN = 6 };
uint32_t w = SIZE_PX + MARGIN * 2;
uint32_t h = SIZE_PX + MARGIN * 2;
uint8_t *pixels = calloc((size_t) w * h, 1);
ttf_bitmap_t bmp;
q48_16_t scale;
uint32_t gid;
int rc;
uint32_t x, y, filled = 0;
if (!pixels) {
printf("test_rasterize U+%04X: calloc failed\n", codepoint);
failures++;
return;
}
bmp.pixels = pixels;
bmp.width = w;
bmp.height = h;
scale = q48_div(q48_from_u64(SIZE_PX), q48_from_u64(font->units_per_em));
gid = ttf_codepoint_to_glyph(font, codepoint);
rc = ttf_rasterize_glyph(font, gid, scale,
q48_from_u64(MARGIN),
q48_from_u64(SIZE_PX + MARGIN),
255, &bmp);
if (rc != TTF_OK) {
printf("test_rasterize U+%04X (%s): ttf_glyph_outline/rasterize rc=%d\n",
codepoint, label, rc);
failures++;
free(pixels);
return;
}
printf("U+%04X (%s) rasterized %ux%u:\n", codepoint, label, w, h);
for (y = 0; y < h; y++) {
putchar('|');
for (x = 0; x < w; x++) {
int on = pixels[y * w + x] != 0;
putchar(on ? '#' : '.');
if (on) filled++;
}
putchar('|');
putchar('\n');
}
printf(" filled=%u / %u px (%.1f%%)\n", filled, w * h, 100.0 * filled / (w * h));
if (filled == 0) {
printf(" FAIL U+%04X (%s): rasterized to nothing\n", codepoint, label);
failures++;
} else if (filled > (w * h * 3) / 4) {
printf(" FAIL U+%04X (%s): implausibly full (%u/%u px) -- looks like a fill-rule bug\n",
codepoint, label, filled, w * h);
failures++;
}
if (pixels[0] != 0) {
printf(" FAIL U+%04X (%s): top-left corner (background) is filled\n", codepoint, label);
failures++;
}
free(pixels);
}
int main(int argc, char **argv) {
FILE *fp;
long size;
@@ -211,6 +283,10 @@ int main(int argc, char **argv) {
test_outlines(&font);
test_rasterize(&font, 0x0041, "'A'");
test_rasterize(&font, 0x002E, "'.'");
test_rasterize(&font, 0x0061, "'a'");
free(buf);
if (failures) {