Add TrueType font support via stb_truetype.h
Integrate stb_truetype.h to rasterize TTF glyphs into 1-bit .FNT v3 format consumed by Win3.x display drivers. Key implementation detail: .FNT bitmaps use column-major byte order (all rows of byte-column 0 first, then byte-column 1, etc.), not row-major. New API: wdrvLoadFontTtf(path, pointSize) loads any TTF at any size. Demo 7 renders Liberation Sans/Serif/Mono at 16/20/24pt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e8a7812233
commit
fbb1cce5c3
11 changed files with 5449 additions and 6 deletions
4
.gitattributes
vendored
4
.gitattributes
vendored
|
|
@ -8,3 +8,7 @@
|
||||||
*.BMP filter=lfs diff=lfs merge=lfs -text
|
*.BMP filter=lfs diff=lfs merge=lfs -text
|
||||||
*.ICO filter=lfs diff=lfs merge=lfs -text
|
*.ICO filter=lfs diff=lfs merge=lfs -text
|
||||||
*.png filter=lfs diff=lfs merge=lfs -text
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.FON filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.fon filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.TTF filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
|
||||||
|
|
@ -80,8 +80,10 @@ windriver/
|
||||||
(386 protected mode with WF_PMODE + WF_CPU386). v2 (0x0200) is rejected at runtime.
|
(386 protected mode with WF_PMODE + WF_CPU386). v2 (0x0200) is rejected at runtime.
|
||||||
- **v3 char table**: at file offset 0x94 (fs30CharOffset), 6-byte entries {WORD width, DWORD offset}.
|
- **v3 char table**: at file offset 0x94 (fs30CharOffset), 6-byte entries {WORD width, DWORD offset}.
|
||||||
The DWORD offset is absolute from the font segment base. Use FntCharEntry30T.
|
The DWORD offset is absolute from the font segment base. Use FntCharEntry30T.
|
||||||
- **Bitmap layout**: per-character contiguous (16 bytes per char for 8x16 font, stride=1 between rows).
|
- **Bitmap layout**: per-character contiguous, **column-major** byte order. For each character,
|
||||||
VGA BIOS 8x16 font (INT 10h AH=11h AL=30h BH=06) is already in this format — no transpose needed.
|
all pixHeight rows of byte-column 0 come first, then all rows of byte-column 1, etc.
|
||||||
|
Address formula: `(byteCol * pixHeight) + row`. For 8px-wide chars (1 byte column), this
|
||||||
|
is identical to row-major. VGA BIOS 8x16 font is already in this format — no transpose needed.
|
||||||
- **lpClipRect must NOT be NULL**: VBESVGA's get_clip unconditionally dereferences lpClipRect
|
- **lpClipRect must NOT be NULL**: VBESVGA's get_clip unconditionally dereferences lpClipRect
|
||||||
(STRBLT.ASM:1008 "We assume that we will never get passed a null rectangle"). Pass a RECT
|
(STRBLT.ASM:1008 "We assume that we will never get passed a null rectangle"). Pass a RECT
|
||||||
covering the full screen (0, 0, 0x7FFF, 0x7FFF).
|
covering the full screen (0, 0, 0x7FFF, 0x7FFF).
|
||||||
|
|
@ -118,8 +120,10 @@ windriver/
|
||||||
Correct v3 offset = v2offset + shift (where shift = newBitmapOff - origBitsOff)
|
Correct v3 offset = v2offset + shift (where shift = newBitmapOff - origBitsOff)
|
||||||
- `wdrvLoadFontFon(path, index)` loads from .FON; `wdrvLoadFontFnt(path)` loads raw .FNT
|
- `wdrvLoadFontFon(path, index)` loads from .FON; `wdrvLoadFontFnt(path)` loads raw .FNT
|
||||||
- `wdrvLoadFontBuiltin()` returns the VGA ROM 8x16 singleton; must NOT be passed to wdrvUnloadFont
|
- `wdrvLoadFontBuiltin()` returns the VGA ROM 8x16 singleton; must NOT be passed to wdrvUnloadFont
|
||||||
|
- `wdrvLoadFontTtf(path, pointSize)` loads TrueType via stb_truetype, rasterizes to 1-bit v3 FNT
|
||||||
- `wdrvExtTextOut` takes a `WdrvFontT font` parameter (NULL = built-in)
|
- `wdrvExtTextOut` takes a `WdrvFontT font` parameter (NULL = built-in)
|
||||||
- Available test fonts in `fon/`: COURE.FON (8x13, 9x16, 12x20), SSERIFE.FON, SERIFE.FON, VGASYS.FON, etc.
|
- Available test fonts in `fon/`: COURE.FON (8x13, 9x16, 12x20), SSERIFE.FON, SERIFE.FON, VGASYS.FON, etc.
|
||||||
|
- Available TTF fonts in `ttf/`: LIBMONO.TTF, LIBSANS.TTF, LIBSERIF.TTF (Liberation family)
|
||||||
|
|
||||||
## Current Demo Status
|
## Current Demo Status
|
||||||
- S3TRIO.DRV, VBESVGA.DRV, VGA.DRV, ET4000.DRV all work: Load → Enable → Draw → Disable → Unload
|
- S3TRIO.DRV, VBESVGA.DRV, VGA.DRV, ET4000.DRV all work: Load → Enable → Draw → Disable → Unload
|
||||||
|
|
@ -128,6 +132,7 @@ windriver/
|
||||||
- Demo 3: Lines/starburst (Output/Polyline) — works
|
- Demo 3: Lines/starburst (Output/Polyline) — works
|
||||||
- Demo 4: Screen-to-screen blit (BitBlt SRCCOPY) — works
|
- Demo 4: Screen-to-screen blit (BitBlt SRCCOPY) — works
|
||||||
- Demo 5: ExtTextOut text rendering — works (VBESVGA.DRV)
|
- Demo 5: ExtTextOut text rendering — works (VBESVGA.DRV)
|
||||||
|
- Demo 7: TrueType font rendering at multiple sizes — works
|
||||||
- VGA.DRV: 640x480 4-plane 16-color mode; limited color palette but functional
|
- VGA.DRV: 640x480 4-plane 16-color mode; limited color palette but functional
|
||||||
- ET4000.DRV: 640x480 8bpp on svga_et4000; software-only, no hw acceleration
|
- ET4000.DRV: 640x480 8bpp on svga_et4000; software-only, no hw acceleration
|
||||||
- Drivers stored in `drivers/` directory, copied to `bin/` during build
|
- Drivers stored in `drivers/` directory, copied to `bin/` during build
|
||||||
|
|
|
||||||
3
Makefile
3
Makefile
|
|
@ -18,7 +18,7 @@ DJGPP_PREFIX ?= $(HOME)/djgpp/djgpp
|
||||||
|
|
||||||
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc
|
||||||
CFLAGS = -Wall -Wextra -O2 -std=gnu99 -Iwin31drv
|
CFLAGS = -Wall -Wextra -O2 -std=gnu99 -Iwin31drv
|
||||||
LDFLAGS =
|
LDFLAGS = -lm
|
||||||
|
|
||||||
# DJGPP binutils need libfl.so.2 which may not be installed system-wide
|
# DJGPP binutils need libfl.so.2 which may not be installed system-wide
|
||||||
export LD_LIBRARY_PATH := $(realpath tools/lib):$(LD_LIBRARY_PATH)
|
export LD_LIBRARY_PATH := $(realpath tools/lib):$(LD_LIBRARY_PATH)
|
||||||
|
|
@ -48,6 +48,7 @@ $(DEMO_EXE): $(DEMO_OBJ) lib | $(BINDIR)
|
||||||
cp tools/TEST.BAT $(BINDIR)/
|
cp tools/TEST.BAT $(BINDIR)/
|
||||||
-cp -n drivers/*.DRV $(BINDIR)/ 2>/dev/null; true
|
-cp -n drivers/*.DRV $(BINDIR)/ 2>/dev/null; true
|
||||||
-cp -n fon/*.FON $(BINDIR)/ 2>/dev/null; true
|
-cp -n fon/*.FON $(BINDIR)/ 2>/dev/null; true
|
||||||
|
-cp -n ttf/*.TTF $(BINDIR)/ 2>/dev/null; true
|
||||||
|
|
||||||
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
$(OBJDIR)/%.o: %.c | $(OBJDIR)
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
|
||||||
46
demo.c
46
demo.c
|
|
@ -395,6 +395,52 @@ static void demoDrawing(WdrvHandleT drv)
|
||||||
wdrvUnloadFont(sysFont);
|
wdrvUnloadFont(sysFont);
|
||||||
logMsg(" Font demo done\n");
|
logMsg(" Font demo done\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Demo 7: TrueType font rendering
|
||||||
|
if (info.hasExtTextOut) {
|
||||||
|
logMsg("Demo 7: TrueType fonts\n");
|
||||||
|
int32_t ret;
|
||||||
|
int16_t textY = 180;
|
||||||
|
|
||||||
|
WdrvFontT ttfSmall = wdrvLoadFontTtf("LIBSANS.TTF", 16);
|
||||||
|
WdrvFontT ttfMedium = wdrvLoadFontTtf("LIBSERIF.TTF", 20);
|
||||||
|
WdrvFontT ttfLarge = wdrvLoadFontTtf("LIBMONO.TTF", 24);
|
||||||
|
|
||||||
|
if (ttfSmall) {
|
||||||
|
const char *msg = "TrueType Sans 16pt: The quick brown fox jumps over the lazy dog";
|
||||||
|
ret = wdrvExtTextOut(drv, 10, textY, msg, (int16_t)strlen(msg),
|
||||||
|
MAKE_RGB(255, 255, 255), MAKE_RGB(0, 0, 128), true, ttfSmall);
|
||||||
|
logMsg(" TTF small ret=%" PRId32 "\n", ret);
|
||||||
|
textY += 22;
|
||||||
|
} else {
|
||||||
|
logMsg(" LIBSANS.TTF 16pt not loaded\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ttfMedium) {
|
||||||
|
const char *msg = "TrueType Serif 20pt: ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
ret = wdrvExtTextOut(drv, 10, textY, msg, (int16_t)strlen(msg),
|
||||||
|
MAKE_RGB(255, 255, 0), MAKE_RGB(128, 0, 0), true, ttfMedium);
|
||||||
|
logMsg(" TTF medium ret=%" PRId32 "\n", ret);
|
||||||
|
textY += 26;
|
||||||
|
} else {
|
||||||
|
logMsg(" LIBSERIF.TTF 20pt not loaded\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ttfLarge) {
|
||||||
|
const char *msg = "TrueType Mono 24pt: 0123456789";
|
||||||
|
ret = wdrvExtTextOut(drv, 10, textY, msg, (int16_t)strlen(msg),
|
||||||
|
MAKE_RGB(0, 255, 255), MAKE_RGB(0, 0, 0), true, ttfLarge);
|
||||||
|
logMsg(" TTF large ret=%" PRId32 "\n", ret);
|
||||||
|
textY += 30;
|
||||||
|
} else {
|
||||||
|
logMsg(" LIBMONO.TTF 24pt not loaded\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
wdrvUnloadFont(ttfSmall);
|
||||||
|
wdrvUnloadFont(ttfMedium);
|
||||||
|
wdrvUnloadFont(ttfLarge);
|
||||||
|
logMsg(" TTF demo done\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
BIN
ttf/LIBMONO.TTF
(Stored with Git LFS)
Normal file
BIN
ttf/LIBMONO.TTF
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
ttf/LIBSANS.TTF
(Stored with Git LFS)
Normal file
BIN
ttf/LIBSANS.TTF
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
ttf/LIBSERIF.TTF
(Stored with Git LFS)
Normal file
BIN
ttf/LIBSERIF.TTF
(Stored with Git LFS)
Normal file
Binary file not shown.
|
|
@ -46,7 +46,7 @@ $(OBJDIR)/log.o: log.c log.h
|
||||||
$(OBJDIR)/neload.o: neload.c neload.h neformat.h wintypes.h log.h
|
$(OBJDIR)/neload.o: neload.c neload.h neformat.h wintypes.h log.h
|
||||||
$(OBJDIR)/thunk.o: thunk.c thunk.h wintypes.h log.h
|
$(OBJDIR)/thunk.o: thunk.c thunk.h wintypes.h log.h
|
||||||
$(OBJDIR)/winstub.o: winstub.c winstub.h thunk.h wintypes.h log.h
|
$(OBJDIR)/winstub.o: winstub.c winstub.h thunk.h wintypes.h log.h
|
||||||
$(OBJDIR)/windrv.o: windrv.c windrv.h wintypes.h winddi.h neformat.h neload.h thunk.h winstub.h log.h
|
$(OBJDIR)/windrv.o: windrv.c windrv.h wintypes.h winddi.h neformat.h neload.h thunk.h winstub.h log.h stb_truetype.h
|
||||||
$(CC) $(WINDRV_CFLAGS) -c -o $@ $<
|
$(CC) $(WINDRV_CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
||||||
5079
win31drv/stb_truetype.h
Normal file
5079
win31drv/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -46,6 +46,10 @@
|
||||||
#include "winstub.h"
|
#include "winstub.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#include "stb_truetype.h"
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Driver instance structure (opaque handle)
|
// Driver instance structure (opaque handle)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
@ -3166,6 +3170,296 @@ WdrvFontT wdrvLoadFontFon(const char *fonPath, int32_t fontIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WdrvFontT wdrvLoadFontTtf(const char *ttfPath, int32_t pointSize)
|
||||||
|
{
|
||||||
|
// Read TTF file into memory
|
||||||
|
FILE *f = fopen(ttfPath, "rb");
|
||||||
|
if (!f) {
|
||||||
|
logErr("windrv: wdrvLoadFontTtf: cannot open '%s'\n", ttfPath);
|
||||||
|
setError(WDRV_ERR_FILE_NOT_FOUND);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long fileSize = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
if (fileSize < 12 || fileSize > 0x1000000) {
|
||||||
|
logErr("windrv: wdrvLoadFontTtf: bad file size %ld\n", fileSize);
|
||||||
|
fclose(f);
|
||||||
|
setError(WDRV_ERR_BAD_FONT);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *ttfBuf = (uint8_t *)malloc((uint32_t)fileSize);
|
||||||
|
if (!ttfBuf) {
|
||||||
|
fclose(f);
|
||||||
|
setError(WDRV_ERR_NO_MEMORY);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(ttfBuf, 1, (uint32_t)fileSize, f) != (size_t)fileSize) {
|
||||||
|
logErr("windrv: wdrvLoadFontTtf: read error\n");
|
||||||
|
free(ttfBuf);
|
||||||
|
fclose(f);
|
||||||
|
setError(WDRV_ERR_BAD_FONT);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
// Initialize stb_truetype
|
||||||
|
stbtt_fontinfo stbFont;
|
||||||
|
if (!stbtt_InitFont(&stbFont, ttfBuf, 0)) {
|
||||||
|
logErr("windrv: wdrvLoadFontTtf: stbtt_InitFont failed\n");
|
||||||
|
free(ttfBuf);
|
||||||
|
setError(WDRV_ERR_BAD_FONT);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute scale for target pixel height
|
||||||
|
float scale = stbtt_ScaleForPixelHeight(&stbFont, (float)pointSize);
|
||||||
|
|
||||||
|
// Get font vertical metrics (unscaled)
|
||||||
|
int stbAscent;
|
||||||
|
int stbDescent;
|
||||||
|
int stbLineGap;
|
||||||
|
stbtt_GetFontVMetrics(&stbFont, &stbAscent, &stbDescent, &stbLineGap);
|
||||||
|
|
||||||
|
int32_t scaledAscent = (int32_t)(stbAscent * scale + 0.5f);
|
||||||
|
int32_t scaledDescent = (int32_t)(-stbDescent * scale + 0.5f);
|
||||||
|
int32_t pixHeight = scaledAscent + scaledDescent;
|
||||||
|
if (pixHeight < 1) {
|
||||||
|
pixHeight = pointSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure all glyphs (chars 32..255)
|
||||||
|
uint16_t nChars = 224;
|
||||||
|
uint8_t firstChar = 32;
|
||||||
|
uint8_t lastChar = 255;
|
||||||
|
|
||||||
|
uint16_t advanceWidths[224];
|
||||||
|
int32_t maxAdvance = 0;
|
||||||
|
int32_t totalAdvance = 0;
|
||||||
|
int32_t maxBitmapW = 0;
|
||||||
|
bool allSameWidth = true;
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < nChars; i++) {
|
||||||
|
int ch = firstChar + i;
|
||||||
|
int advance;
|
||||||
|
int lsb;
|
||||||
|
stbtt_GetCodepointHMetrics(&stbFont, ch, &advance, &lsb);
|
||||||
|
|
||||||
|
int32_t advW = (int32_t)(advance * scale + 0.5f);
|
||||||
|
if (advW < 1) {
|
||||||
|
advW = 1;
|
||||||
|
}
|
||||||
|
advanceWidths[i] = (uint16_t)advW;
|
||||||
|
totalAdvance += advW;
|
||||||
|
if (advW > maxAdvance) {
|
||||||
|
maxAdvance = advW;
|
||||||
|
}
|
||||||
|
if (i > 0 && advanceWidths[i] != advanceWidths[0]) {
|
||||||
|
allSameWidth = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check bitmap bounding box too
|
||||||
|
int x0;
|
||||||
|
int y0;
|
||||||
|
int x1;
|
||||||
|
int y1;
|
||||||
|
stbtt_GetCodepointBitmapBox(&stbFont, ch, scale, scale, &x0, &y0, &x1, &y1);
|
||||||
|
int32_t bw = x1 - x0;
|
||||||
|
if (bw > maxBitmapW) {
|
||||||
|
maxBitmapW = bw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fsWidthBytes = sum of all per-character byte strides (matches .FON convention)
|
||||||
|
uint32_t widthBytesSum = 0;
|
||||||
|
for (int32_t i = 0; i < nChars; i++) {
|
||||||
|
uint16_t s = (advanceWidths[i] + 7) / 8;
|
||||||
|
if (s < 1) {
|
||||||
|
s = 1;
|
||||||
|
}
|
||||||
|
widthBytesSum += s;
|
||||||
|
}
|
||||||
|
uint16_t fsWidthBytes = (uint16_t)widthBytesSum;
|
||||||
|
|
||||||
|
// Compute v3 layout with per-character bitmap sizes.
|
||||||
|
// Each character's bitmap has its own row stride = ceil(charWidth/8).
|
||||||
|
uint32_t charTableOff = 0x0094;
|
||||||
|
uint32_t bitmapOff = charTableOff + (uint32_t)(nChars + 1) * 6;
|
||||||
|
|
||||||
|
uint32_t charOffsets[225];
|
||||||
|
uint32_t curOff = bitmapOff;
|
||||||
|
for (int32_t i = 0; i < nChars; i++) {
|
||||||
|
charOffsets[i] = curOff;
|
||||||
|
uint16_t byteStride = (advanceWidths[i] + 7) / 8;
|
||||||
|
if (byteStride < 1) {
|
||||||
|
byteStride = 1;
|
||||||
|
}
|
||||||
|
curOff += (uint32_t)byteStride * (uint32_t)pixHeight;
|
||||||
|
}
|
||||||
|
charOffsets[nChars] = charOffsets[0]; // sentinel reuses char 0
|
||||||
|
|
||||||
|
uint32_t devNameOff = curOff;
|
||||||
|
uint32_t faceNameOff = devNameOff + 1;
|
||||||
|
|
||||||
|
// Extract face name from filename stem
|
||||||
|
const char *baseName = ttfPath;
|
||||||
|
for (const char *p = ttfPath; *p; p++) {
|
||||||
|
if (*p == '/' || *p == '\\') {
|
||||||
|
baseName = p + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char faceName[64];
|
||||||
|
strncpy(faceName, baseName, sizeof(faceName) - 1);
|
||||||
|
faceName[sizeof(faceName) - 1] = '\0';
|
||||||
|
char *dot = strrchr(faceName, '.');
|
||||||
|
if (dot) {
|
||||||
|
*dot = '\0';
|
||||||
|
}
|
||||||
|
uint32_t faceNameLen = (uint32_t)strlen(faceName);
|
||||||
|
uint32_t totalSize = faceNameOff + faceNameLen + 1;
|
||||||
|
|
||||||
|
// Allocate 16-bit accessible block
|
||||||
|
uint32_t linearOut;
|
||||||
|
uint16_t sel = alloc16BitBlock(totalSize, &linearOut);
|
||||||
|
if (sel == 0) {
|
||||||
|
free(ttfBuf);
|
||||||
|
setError(WDRV_ERR_NO_MEMORY);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *base = (uint8_t *)linearOut;
|
||||||
|
|
||||||
|
// Fill .FNT v3 header
|
||||||
|
FntHeader16T *hdr = (FntHeader16T *)base;
|
||||||
|
hdr->fsVersion = 0x0300;
|
||||||
|
hdr->fsSize = totalSize;
|
||||||
|
hdr->fsType = 0; // raster
|
||||||
|
hdr->fsPoints = (uint16_t)pointSize;
|
||||||
|
hdr->fsVertRes = 96;
|
||||||
|
hdr->fsHorizRes = 96;
|
||||||
|
hdr->fsAscent = (uint16_t)scaledAscent;
|
||||||
|
hdr->fsInternalLeading = 0;
|
||||||
|
hdr->fsExternalLeading = 0;
|
||||||
|
hdr->fsItalic = 0;
|
||||||
|
hdr->fsUnderline = 0;
|
||||||
|
hdr->fsStrikeOut = 0;
|
||||||
|
hdr->fsWeight = 400;
|
||||||
|
hdr->fsCharSet = 0; // ANSI
|
||||||
|
hdr->fsPixWidth = allSameWidth ? advanceWidths[0] : 0;
|
||||||
|
hdr->fsPixHeight = (uint16_t)pixHeight;
|
||||||
|
hdr->fsPitchAndFamily = allSameWidth ? 0x30 : 0x01;
|
||||||
|
hdr->fsAvgWidth = (uint16_t)(totalAdvance / nChars);
|
||||||
|
hdr->fsMaxWidth = (uint16_t)maxAdvance;
|
||||||
|
hdr->fsFirstChar = firstChar;
|
||||||
|
hdr->fsLastChar = lastChar;
|
||||||
|
hdr->fsDefaultChar = (uint8_t)('.' - firstChar);
|
||||||
|
hdr->fsBreakChar = (uint8_t)(' ' - firstChar);
|
||||||
|
hdr->fsWidthBytes = fsWidthBytes;
|
||||||
|
hdr->fsDevice = devNameOff;
|
||||||
|
hdr->fsFace = faceNameOff;
|
||||||
|
hdr->fsBitsPointer = ((uint32_t)sel << 16) | bitmapOff;
|
||||||
|
hdr->fsBitsOffset = bitmapOff;
|
||||||
|
|
||||||
|
// v3 extension at 0x76 already zeroed by calloc
|
||||||
|
|
||||||
|
// Fill v3 char table at 0x94
|
||||||
|
FntCharEntry30T *charTable = (FntCharEntry30T *)(base + charTableOff);
|
||||||
|
for (int32_t i = 0; i <= nChars; i++) {
|
||||||
|
int32_t idx = (i < nChars) ? i : 0; // sentinel reuses char 0
|
||||||
|
charTable[i].width = advanceWidths[idx];
|
||||||
|
charTable[i].offset = charOffsets[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rasterize glyphs and threshold to 1-bit
|
||||||
|
for (int32_t i = 0; i < nChars; i++) {
|
||||||
|
int ch = firstChar + i;
|
||||||
|
int bw;
|
||||||
|
int bh;
|
||||||
|
int xoff;
|
||||||
|
int yoff;
|
||||||
|
unsigned char *bitmap = stbtt_GetCodepointBitmap(
|
||||||
|
&stbFont, scale, scale, ch, &bw, &bh, &xoff, &yoff);
|
||||||
|
|
||||||
|
if (!bitmap || bw <= 0 || bh <= 0) {
|
||||||
|
if (bitmap) {
|
||||||
|
stbtt_FreeBitmap(bitmap, NULL);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position glyph within the fixed-height cell.
|
||||||
|
// xoff/yoff are relative to the pen position; use directly and
|
||||||
|
// let the bounds checks clip anything outside the cell.
|
||||||
|
int32_t cellY = scaledAscent + yoff;
|
||||||
|
|
||||||
|
uint16_t charStride = (advanceWidths[i] + 7) / 8;
|
||||||
|
if (charStride < 1) {
|
||||||
|
charStride = 1;
|
||||||
|
}
|
||||||
|
uint8_t *dst = base + charOffsets[i];
|
||||||
|
|
||||||
|
// .FNT bitmap format is column-major: all pixHeight rows of
|
||||||
|
// byte-column 0 first, then all rows of byte-column 1, etc.
|
||||||
|
// Layout: dst[(byteCol * pixHeight) + row] bit = MSB-first
|
||||||
|
for (int row = 0; row < bh; row++) {
|
||||||
|
int32_t dstRow = cellY + row;
|
||||||
|
if (dstRow < 0 || dstRow >= pixHeight) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int col = 0; col < bw; col++) {
|
||||||
|
int32_t dstCol = xoff + col;
|
||||||
|
if (dstCol < 0 || dstCol >= (int32_t)(charStride * 8)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uint8_t alpha = bitmap[row * bw + col];
|
||||||
|
if (alpha >= 128) {
|
||||||
|
uint32_t byteCol = (uint32_t)dstCol / 8;
|
||||||
|
dst[byteCol * pixHeight + dstRow] |= (0x80 >> (dstCol & 7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbtt_FreeBitmap(bitmap, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write device name (empty) and face name
|
||||||
|
base[devNameOff] = '\0';
|
||||||
|
memcpy(base + faceNameOff, faceName, faceNameLen + 1);
|
||||||
|
|
||||||
|
// Done with TTF data
|
||||||
|
free(ttfBuf);
|
||||||
|
|
||||||
|
// Allocate font handle
|
||||||
|
struct WdrvFontS *font = (struct WdrvFontS *)calloc(1, sizeof(struct WdrvFontS));
|
||||||
|
if (!font) {
|
||||||
|
free16BitBlock(sel, linearOut);
|
||||||
|
setError(WDRV_ERR_NO_MEMORY);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
font->fontSel = sel;
|
||||||
|
font->fontLinear = linearOut;
|
||||||
|
font->fontSize = totalSize;
|
||||||
|
font->pixHeight = (uint16_t)pixHeight;
|
||||||
|
font->pixWidth = allSameWidth ? advanceWidths[0] : 0;
|
||||||
|
font->fsVersion = 0x0300;
|
||||||
|
strncpy(font->faceName, faceName, sizeof(font->faceName) - 1);
|
||||||
|
font->faceName[sizeof(font->faceName) - 1] = '\0';
|
||||||
|
|
||||||
|
dbg("windrv: wdrvLoadFontTtf: '%s' %dpt -> %dx%d (max=%d avg=%d) sel=%04X size=%" PRIu32 "\n",
|
||||||
|
faceName, (int)pointSize,
|
||||||
|
allSameWidth ? (int)advanceWidths[0] : 0, (int)pixHeight,
|
||||||
|
(int)maxAdvance, (int)(totalAdvance / nChars),
|
||||||
|
sel, totalSize);
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void wdrvUnloadFont(WdrvFontT font)
|
void wdrvUnloadFont(WdrvFontT font)
|
||||||
{
|
{
|
||||||
if (!font || font == &gBuiltinFont) {
|
if (!font || font == &gBuiltinFont) {
|
||||||
|
|
|
||||||
|
|
@ -174,8 +174,13 @@ WdrvFontT wdrvLoadFontFnt(const char *fntPath);
|
||||||
// The returned handle must NOT be passed to wdrvUnloadFont.
|
// The returned handle must NOT be passed to wdrvUnloadFont.
|
||||||
WdrvFontT wdrvLoadFontBuiltin(void);
|
WdrvFontT wdrvLoadFontBuiltin(void);
|
||||||
|
|
||||||
// Unload a font previously loaded with wdrvLoadFontFon or wdrvLoadFontFnt.
|
// Load a font from a TrueType (.TTF) file at the given point size.
|
||||||
// Silently ignores NULL and the built-in font.
|
// Rasterizes glyphs to 1-bit bitmaps and packs into .FNT v3 format.
|
||||||
|
// Returns NULL on failure (call wdrvGetLastError for details).
|
||||||
|
WdrvFontT wdrvLoadFontTtf(const char *ttfPath, int32_t pointSize);
|
||||||
|
|
||||||
|
// Unload a font previously loaded with wdrvLoadFontFon, wdrvLoadFontFnt,
|
||||||
|
// or wdrvLoadFontTtf. Silently ignores NULL and the built-in font.
|
||||||
void wdrvUnloadFont(WdrvFontT font);
|
void wdrvUnloadFont(WdrvFontT font);
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue