Support Unicode symbols in VESA modes

This commit is contained in:
Ray Chason
2022-10-08 18:50:38 -04:00
parent 334fd76ab4
commit fb88488583
5 changed files with 419 additions and 44 deletions

276
sys/msdos/font.c Executable file
View File

@@ -0,0 +1,276 @@
/* Maintain a data structure describing a monospaced bitmap font */
#include "hack.h"
#include "integer.h"
#include "font.h"
static uint32 read_u32(const unsigned char *);
static void add_unicode_index(
struct BitmapFont *font,
uint32 ch,
unsigned index);
static uint32 *uni_8to32(const char *);
struct BitmapFont *
load_font(const char *filename)
{
FILE *fp;
struct BitmapFont *font = NULL;
unsigned char header[32];
size_t size;
uint32 magic;
uint32 version;
uint32 headersize;
uint32 flags;
uint32 length;
uint32 charsize;
uint32 height;
uint32 width;
uint32 bwidth, memsize;
uint32 i;
fp = fopen(filename, "rb");
if (fp == NULL) goto error;
/* Read the PSF header */
size = fread(header, 1, sizeof(header), fp);
if (size != sizeof(header)) goto error;
/* Convert from little endian order */
magic = read_u32(header + 0);
if (magic != 0x864AB572) goto error;
version = read_u32(header + 4);
if (version != 0) goto error;
headersize = read_u32(header + 8);
if (headersize < sizeof(header)) goto error;
flags = read_u32(header + 12);
length = read_u32(header + 16);
charsize = read_u32(header + 20);
height = read_u32(header + 24);
width = read_u32(header + 28);
/* Check that the declared character size can hold the declared width
and height */
bwidth = (width + 7) / 8;
memsize = bwidth * height;
if (memsize > charsize) goto error;
/* Allocate a font structure */
font = (struct BitmapFont *) alloc(sizeof(struct BitmapFont));
memset(font, 0, sizeof(struct BitmapFont));
/* Dimensions of the font */
font->width = width;
font->height = height;
font->num_glyphs = length;
/* The glyph array */
font->glyphs = (unsigned char **) alloc(length * sizeof(unsigned char *));
memset(font->glyphs, 0, length * sizeof(unsigned char *));
/* Read the glyphs */
fseek(fp, headersize, SEEK_SET);
for (i = 0; i < length; ++i) {
font->glyphs[i] = (unsigned char *) alloc(memsize);
size = fread(font->glyphs[i], 1, memsize, fp);
if (size != memsize) goto error;
fseek(fp, charsize - memsize, SEEK_CUR);
}
if (flags & 0x01) {
/* Read the Unicode table */
char buf[128], buf2[128+1];
unsigned bufsize, strsize;
char *p;
uint32 *codepoints;
bufsize = 0;
i = 0;
while (i < length) {
unsigned j;
size = fread(buf + bufsize, 1, sizeof(buf) - bufsize, fp);
if (ferror(fp)) goto error;
bufsize += size;
if (bufsize == 0) goto error; /* unexpected EOF */
p = memchr(buf, 0xFF, bufsize);
if (p != NULL) { /* end marker found */
strsize = p - buf;
memcpy(buf2, buf, strsize);
buf2[strsize] = '\0';
bufsize -= strsize + 1;
memmove(buf, buf + strsize + 1, bufsize);
} else { /* partial string */
strsize = bufsize - 1;
/* Roll back to character boundary in case of partial character */
while (strsize != 0 && (buf[strsize] & 0xC0) == 0x80)
--strsize;
if (strsize == 0) /* avoid infinite loop */
strsize = (bufsize < 4) ? bufsize : 4;
memcpy(buf2, buf, strsize);
buf2[strsize] = '\0';
bufsize -= strsize;
memmove(buf, buf + strsize, bufsize);
}
codepoints = uni_8to32(buf2);
for (j = 0; codepoints[j] != 0; ++j) {
add_unicode_index(font, codepoints[j], i);
}
free(codepoints);
if (p != NULL)
++i;
}
} else {
/* Fake a Unicode table, assuming that ASCII glyphs are in the
expected places */
for (i = 0x20; i <= 0x7E; ++i) {
add_unicode_index(font, i, i);
}
}
fclose(fp);
return font;
error:
if (fp != NULL) fclose(fp);
free_font(font);
return NULL;
}
void
free_font(struct BitmapFont *font)
{
unsigned i, j;
if (font == NULL) return;
if (font->glyphs != NULL) {
for (i = 0; i < font->num_glyphs; ++i)
free(font->glyphs[i]);
free(font->glyphs);
}
for (i = 0; i < SIZE(font->unicode); ++i) {
if (font->unicode[i] == NULL) continue;
for (j = 0; j < 256; ++j)
free(font->unicode[i][j]);
free(font->unicode[i]);
}
free(font);
}
const unsigned char *
get_font_glyph(struct BitmapFont *font, uint32 ch, boolean unicode)
{
unsigned index;
if (unicode) {
index = 0;
if (ch <= 0x10FFFF && (ch & 0xFFFFD800) != 0xD800) {
unsigned i, j, k;
i = (unsigned) (ch >> 16);
j = (unsigned) ((ch >> 8) & 0xFF);
k = (unsigned) (ch & 0xFF);
if (font->unicode[i] != NULL
&& font->unicode[i][j] != NULL) {
index = font->unicode[i][j][k];
}
}
} else {
index = ch;
}
if (index >= font->num_glyphs)
index = 0;
return font->glyphs[index];
}
static void
add_unicode_index(struct BitmapFont *font, uint32 ch, unsigned index)
{
unsigned i, j, k;
i = (unsigned) (ch >> 16);
j = (unsigned) ((ch >> 8) & 0xFF);
k = (unsigned) (ch & 0xFF);
if (font->unicode[i] == NULL) {
/* Create the second level node */
font->unicode[i] = (unsigned **) alloc(256 * sizeof(unsigned *));
memset(font->unicode[i], 0, 256 * sizeof(unsigned *));
}
if (font->unicode[i][j] == NULL) {
/* Create the third level node */
font->unicode[i][j] = (unsigned *) alloc(256 * sizeof(unsigned));
memset(font->unicode[i][j], 0, 256 * sizeof(unsigned));
}
font->unicode[i][j][k] = index;
}
static uint32
read_u32(const unsigned char *buf)
{
return ((uint32) buf[0] << 0)
| ((uint32) buf[1] << 8)
| ((uint32) buf[2] << 16)
| ((uint32) buf[3] << 24);
}
static uint32 *
uni_8to32(const char *inp)
{
size_t i, j;
uint32 *out;
/* Output string */
out = (uint32 *) alloc((strlen(inp) + 1) * sizeof(out[0]));
i = 0;
j = 0;
while (inp[i] != 0) {
unsigned char byte = inp[i++];
uint32 ch32;
uint32 min = 0;
unsigned count = 0;
if (byte < 0x80) {
ch32 = byte;
} else if (byte < 0xC0) {
ch32 = 0xFFFD;
} else if (byte < 0xE0) {
ch32 = byte & 0x1F;
min = 0x80;
count = 1;
} else if (byte < 0xF0) {
ch32 = byte & 0x0F;
min = 0x800;
count = 2;
} else if (byte < 0xF5) {
ch32 = byte & 0x07;
min = 0x10000;
count = 3;
} else {
ch32 = 0xFFFD;
}
for (; count != 0; --count) {
byte = inp[i];
if ((byte & 0xC0) != 0x80) {
break;
}
++i;
ch32 = (ch32 << 6) | (byte & 0x3F);
}
if (count != 0 || ch32 < min || ((ch32 & 0xFFFFF800) == 0xD800)) {
ch32 = 0xFFFD;
}
out[j++] = ch32;
}
out[j] = 0;
return out;
}

53
sys/msdos/font.h Executable file
View File

@@ -0,0 +1,53 @@
/* Maintain a data structure describing a monospaced bitmap font */
#ifndef FONT_H
#define FONT_H
#include "integer.h"
/*
* The file format is Linux PSF, version 2. Version 1 is not supported.
* Actual Linux fonts are restricted to 256 or 512 glyphs; for NetHack, the
* font can have any number of glyphs. The Unicode map is expected to be
* present, but combining sequences are not supported.
* The fonts supplied for use with this data structure have the first 256
* glyphs arranged according to IBM code page 437, for simpler support of
* the IBM handling mode for the map.
*/
/* For Unicode lookup, a three level tree provides constant-time access to
the glyphs without using an excessive amount of memory.
The root has seventeen entries, one for each plane of Unicode. Most fonts
will populate only plane 0, and Unicode defines only planes 0, 1, 2, 3, 15
and 16.
The second level has 256 entries, each pointing to a third level node with
256 entries. Each third level entry has type unsigned, and gives the index
of the glyph.
Given the Unicode code point, we can use bits 20 through 16 to index the
root, bits 15 through 8 for the second level and bits 7 through 0 for the
third level. */
struct BitmapFont {
/* Dimensions of a single glyph */
unsigned width;
unsigned height;
/* The glyphs, in the order that they appear in the font */
/* IBM handling will index the glyphs this way */
/* glyph points to an allocated array, each element of which points to
another allocated array */
unsigned num_glyphs;
unsigned char **glyphs;
/* The root node of the Unicode tree */
unsigned **unicode[17];
};
extern struct BitmapFont *load_font(const char *filename);
extern void free_font(struct BitmapFont *font);
extern const unsigned char *get_font_glyph(
struct BitmapFont *font,
uint32 ch,
boolean unicode);
#endif

View File

@@ -16,13 +16,14 @@
#include "vesa.h"
#include "wintty.h"
#include "tileset.h"
#include "font.h"
#define FIRST_TEXT_COLOR 240
extern int total_tiles_used, Tile_corr, Tile_unexplored; /* from tile.c */
struct VesaCharacter {
int colour;
int chr;
uint32 chr;
};
static unsigned long vesa_SetWindow(int window, unsigned long offset);
@@ -47,13 +48,13 @@ static boolean vesa_SetHardPalette(const struct Pixel *);
static boolean vesa_SetSoftPalette(const struct Pixel *);
static void vesa_DisplayCell(int, int, int);
static unsigned vesa_FindMode(unsigned long mode_addr, unsigned bits);
static void vesa_WriteChar(int, int, int, int);
static void vesa_WriteCharXY(int, int, int, int);
static void vesa_WriteChar(uint32, int, int, int);
static void vesa_WriteCharXY(uint32, int, int, int);
static void vesa_WriteCharTransparent(int, int, int, int);
static void vesa_WriteTextRow(int pixx, int pixy,
struct VesaCharacter const *t_row, unsigned t_row_width);
static boolean vesa_GetCharPixel(int, unsigned, unsigned);
static unsigned char vesa_GetCharPixelRow(int, unsigned, unsigned);
static unsigned char vesa_GetCharPixelRow(uint32, unsigned, unsigned);
static unsigned long vesa_DoublePixels(unsigned long);
static unsigned long vesa_TriplePixels(unsigned long);
static void vesa_WriteStr(const char *, int, int, int, int);
@@ -81,7 +82,7 @@ static unsigned char __far *font;
static struct map_struct {
int glyph;
int ch;
uint32 ch;
int attr;
unsigned special;
short int tileidx;
@@ -151,6 +152,7 @@ static unsigned vesa_char_width = 8, vesa_char_height = 16;
static unsigned vesa_oview_width, vesa_oview_height;
static unsigned char **vesa_tiles;
static unsigned char **vesa_oview_tiles;
static struct BitmapFont *vesa_font;
#ifdef SIMULATE_CURSOR
static unsigned long *undercursor;
@@ -670,12 +672,17 @@ vesa_xputc(char ch, int attr)
void
vesa_xputg(const glyph_info *glyphinfo)
{
int glyphnum = glyphinfo->glyph, ch = glyphinfo->ttychar;
int glyphnum = glyphinfo->glyph;
uint32 ch = (uchar) glyphinfo->ttychar;
unsigned special = glyphinfo->gm.glyphflags;
int col, row;
int attr;
int ry;
if (SYMHANDLING(H_UTF8) && glyphinfo->gm.u && glyphinfo->gm.u->utf8str) {
ch = glyphinfo->gm.u->utf32ch;
}
row = currow;
col = curcol;
if ((col < 0 || col >= COLNO)
@@ -689,7 +696,7 @@ vesa_xputg(const glyph_info *glyphinfo)
attr = (g_attribute == 0) ? attrib_gr_normal : g_attribute;
map[ry][col].attr = attr;
if (iflags.traditional_view) {
vesa_WriteChar((unsigned char) ch, col, row, attr);
vesa_WriteChar(ch, col, row, attr);
} else {
if ((col >= clipx) && (col <= clipxmax)
&& (ry >= clipy) && (ry <= clipymax)) {
@@ -1011,6 +1018,7 @@ vesa_Init(void)
unsigned i;
unsigned num_pixels, num_oview_pixels;
const char *tile_file;
const char *font_name;
int tilefailure = 0;
if (inited) return;
@@ -1090,22 +1098,48 @@ vesa_Init(void)
vesa_oview_height = (unsigned) iflags.wc_tile_height;
}
/* Use the map font size to set the font size */
/* Supported sizes are 8x16, 16x32, 24x48 and 32x64 */
vesa_char_height = iflags.wc_fontsiz_map;
if (vesa_char_height <= 0 || vesa_char_height > vesa_y_res / 30) {
vesa_char_height = vesa_y_res / 30;
}
if (vesa_char_height < 32) {
vesa_char_height = 16;
} else if (vesa_char_height < 48) {
vesa_char_height = 32;
} else if (vesa_char_height < 64) {
vesa_char_height = 48;
/* Load a font of size appropriate to the screen size */
if (vesa_x_res >= 1280 && vesa_y_res >= 960)
font_name = "ter-u32b.psf";
else if (vesa_x_res >= 1120 && vesa_y_res >= 840)
font_name = "ter-u28b.psf";
else if (vesa_x_res >= 960 && vesa_y_res >= 720)
font_name = "ter-u24b.psf";
else if (vesa_x_res >= 880 && vesa_y_res >= 660)
font_name = "ter-u22b.psf";
else if (vesa_x_res >= 800 && vesa_y_res >= 600)
font_name = "ter-u20b.psf";
else if (vesa_x_res >= 720 && vesa_y_res >= 540)
font_name = "ter-u18b.psf";
else
font_name = "ter-u16v.psf";
if (iflags.wc_font_map != NULL && iflags.wc_font_map[0] != '\0')
font_name = iflags.wc_font_map;
free_font(vesa_font);
vesa_font = load_font(font_name);
/* if load_font fails, vesa_font is NULL and we'll fall back to the font
defined in ROM */
if (vesa_font != NULL) {
vesa_char_width = vesa_font->width;
vesa_char_height = vesa_font->height;
} else {
vesa_char_height = 64;
/* Use the map font size to set the font size */
/* Supported sizes are 8x16, 16x32, 24x48 and 32x64 */
vesa_char_height = iflags.wc_fontsiz_map;
if (vesa_char_height <= 0 || vesa_char_height > vesa_y_res / 30) {
vesa_char_height = vesa_y_res / 30;
}
if (vesa_char_height < 32) {
vesa_char_height = 16;
} else if (vesa_char_height < 48) {
vesa_char_height = 32;
} else if (vesa_char_height < 64) {
vesa_char_height = 48;
} else {
vesa_char_height = 64;
}
vesa_char_width = vesa_char_height / 2;
}
vesa_char_width = vesa_char_height / 2;
/* Process tiles for the current video mode */
vesa_tiles = (unsigned char **) alloc(total_tiles_used * sizeof(void *));
@@ -1508,7 +1542,7 @@ vesa_FindMode(unsigned long mode_addr, unsigned bits)
*
*/
static void
vesa_WriteChar(int chr, int col, int row, int colour)
vesa_WriteChar(uint32 chr, int col, int row, int colour)
{
int pixx, pixy;
@@ -1527,7 +1561,7 @@ vesa_WriteChar(int chr, int col, int row, int colour)
* transparency
*/
static void
vesa_WriteCharXY(int chr, int pixx, int pixy, int colour)
vesa_WriteCharXY(uint32 chr, int pixx, int pixy, int colour)
{
/* Flush if cache is full or if not contiguous to the last character */
if (chr_cache_size >= SIZE(chr_cache)) {
@@ -1604,7 +1638,7 @@ vesa_WriteTextRow(int pixx, int pixy, struct VesaCharacter const *t_row,
/* Second loop: draw one raster line of one character */
x = 0;
for (i = 0; i < t_row_width; ++i) {
int chr = t_row[i].chr;
uint32 chr = t_row[i].chr;
int colour = t_row[i].colour + FIRST_TEXT_COLOR;
/* Preprocess the foreground color */
if (vesa_pixel_bytes == 1) {
@@ -1655,8 +1689,9 @@ vesa_GetCharPixel(int ch, unsigned x, unsigned y)
}
static unsigned char
vesa_GetCharPixelRow(int ch, unsigned x, unsigned y)
vesa_GetCharPixelRow(uint32 ch, unsigned x, unsigned y)
{
unsigned fnt_width;
unsigned x1;
unsigned char fnt;
size_t offset;
@@ -1664,30 +1699,38 @@ vesa_GetCharPixelRow(int ch, unsigned x, unsigned y)
if (x >= vesa_char_width) return 0;
if (y >= vesa_char_height) return 0;
fnt_width = (vesa_char_width + 7) / 8;
x1 = x / 8;
const unsigned char __far *fp;
if (vesa_font != NULL) {
const unsigned char *fp;
if (ch < 0 || 255 < ch) return FALSE;
offset = ch * 16 + (y * 16 / vesa_char_height);
fp = font;
fnt = READ_ABSOLUTE((fp + offset));
offset = y * fnt_width + x1;
fp = get_font_glyph(vesa_font, ch, SYMHANDLING(H_UTF8));
fnt = fp[offset];
} else {
const unsigned char __far *fp;
if (vesa_char_width != 8) {
unsigned long fnt2 = fnt;
unsigned width = vesa_char_width;
if (width % 3 == 0) {
fnt2 = vesa_TriplePixels(fnt2);
width /= 3;
if (255 < ch) return 0;
offset = (ch * vesa_char_height + y) * fnt_width + x1;
fp = font;
fnt = READ_ABSOLUTE((fp + offset));
if (vesa_char_width != 8) {
unsigned long fnt2 = fnt;
unsigned width = vesa_char_width;
if (width % 3 == 0) {
fnt2 = vesa_TriplePixels(fnt2);
width /= 3;
}
while (width > 8) {
fnt2 = vesa_DoublePixels(fnt2);
width /= 2;
}
fnt2 <<= 32 - vesa_char_width;
fnt = (unsigned char)(fnt2 >> (24 - 8 * x1));
}
while (width > 8) {
fnt2 = vesa_DoublePixels(fnt2);
width /= 2;
}
fnt2 <<= 32 - vesa_char_width;
fnt = (unsigned char)(fnt2 >> (24 - 8 * x1));
}
return fnt;
}

View File

@@ -6,6 +6,7 @@
ifdef CROSS_TO_MSDOS
#
$(TARGETPFX)msdos.o : ../sys/msdos/msdos.c $(HACK_H)
$(TARGETPFX)font.o : ../sys/msdos/font.c ../sys/msdos/font.h $(HACK_H)
$(TARGETPFX)pckeys.o : ../sys/msdos/pckeys.c $(HACK_H)
$(TARGETPFX)pctiles.o : ../sys/msdos/pctiles.c ../sys/msdos/portio.h $(HACK_H)
$(TARGETPFX)video.o : ../sys/msdos/video.c ../sys/msdos/portio.h $(HACK_H)
@@ -14,7 +15,7 @@ $(TARGETPFX)vidtxt.o : ../sys/msdos/vidtxt.c ../sys/msdos/portio.h \
$(TARGETPFX)vidvga.o : ../sys/msdos/vidvga.c ../sys/msdos/portio.h \
../win/share/tile.h ../include/tileset.h $(HACK_H)
$(TARGETPFX)vidvesa.o : ../sys/msdos/vidvesa.c ../sys/msdos/portio.h \
../win/share/tile.h ../include/tileset.h $(HACK_H)
../win/share/tile.h ../include/tileset.h ../sys/msdos/font.h $(HACK_H)
$(TARGETPFX)vidstub.o : ../sys/msdos/vidvesa.c ../sys/msdos/portio.h \
$(HACK_H)
$(TARGETPFX)tile.o : tile.c

View File

@@ -185,6 +185,7 @@ override SYSSRC = ../sys/share/pcmain.c ../sys/msdos/msdos.c \
../sys/share/pcunix.c ../sys/msdos/video.c \
../sys/msdos/vidtxt.c ../sys/msdos/pckeys.c \
../sys/msdos/vidvga.c ../sys/msdos/vidvesa.c \
../sys/msdos/font.c \
../win/share/bmptiles.c ../win/share/giftiles.c \
../win/share/tileset.c
override SYSOBJ= $(TARGETPFX)pcmain.o $(TARGETPFX)msdos.o \
@@ -192,6 +193,7 @@ override SYSOBJ= $(TARGETPFX)pcmain.o $(TARGETPFX)msdos.o \
$(TARGETPFX)pcunix.o $(TARGETPFX)video.o \
$(TARGETPFX)vidtxt.o $(TARGETPFX)pckeys.o \
$(TARGETPFX)vidvga.o $(TARGETPFX)vidvesa.o \
$(TARGETPFX)font.o \
$(TARGETPFX)bmptiles.o $(TARGETPFX)giftiles.o \
$(TARGETPFX)tileset.o $(TARGETPFX)tile.o
override WINLIB=