bitmap font renderer

This commit is contained in:
nakst 2021-12-28 21:24:55 +00:00
parent eb877e864e
commit a16658eed0
13 changed files with 378 additions and 171 deletions

View File

@ -139,7 +139,7 @@ int FontPreviewMessage(EsElement *element, EsMessage *message) {
EsElementGetTextStyle(element, &runs[0].style);
runs[0].style.font.family = element->userData.u;
runs[0].style.font.weight = element->instance->fontVariant % 10;
runs[0].style.font.italic = element->instance->fontVariant / 10;
if (element->instance->fontVariant / 10 == 1) runs[0].style.font.flags |= ES_FONT_ITALIC;
runs[0].style.size = element->instance->fontSize;
runs[1].offset = element->instance->previewTextBytes;
EsTextPlan *plan = EsTextPlanCreate(element, &properties, bounds, element->instance->previewText, runs, 1);

View File

@ -248,7 +248,7 @@ void CreateStyledTextDisplay(Instance *instance, EsStyle *style, uint64_t flags
runs[i].offset = instance->spans[i].offset;
EsElementGetTextStyle(display, &runs[i].style);
if (instance->spans[i].link) { runs[i].style.decorations |= ES_TEXT_DECORATION_UNDERLINE; runs[i].style.color = COLOR_TEXT_LINK; }
if (instance->spans[i].em) runs[i].style.font.italic = true;
if (instance->spans[i].em) runs[i].style.font.flags |= ES_FONT_ITALIC;
if (instance->spans[i].strong) runs[i].style.font.weight = 7;
if (instance->spans[i].monospaced) runs[i].style.font.family = ES_FONT_MONOSPACED;
runs[i].style.decorationsColor = runs[i].style.color;

View File

@ -261,7 +261,8 @@ const void *const apiTable[] = {
};
#ifdef PAUSE_ON_USERLAND_CRASH
ES_EXTERN_C uintptr_t APISyscallCheckForCrash(uintptr_t argument0, uintptr_t argument1, uintptr_t argument2, uintptr_t unused, uintptr_t argument3, uintptr_t argument4) {
__attribute__((no_instrument_function))
uintptr_t APISyscallCheckForCrash(uintptr_t argument0, uintptr_t argument1, uintptr_t argument2, uintptr_t unused, uintptr_t argument3, uintptr_t argument4) {
uintptr_t returnValue = _APISyscall(argument0, argument1, argument2, unused, argument3, argument4);
EsProcessState state;
_APISyscall(ES_SYSCALL_PROCESS_GET_STATE, ES_CURRENT_PROCESS, (uintptr_t) &state, 0, 0, 0);
@ -1486,28 +1487,26 @@ extern "C" void _start(EsProcessStartupInformation *_startupInformation) {
EsMemoryCopy(ES_API_BASE, apiTable, sizeof(apiTable));
}
{
// Initialise the API.
// Initialise the API.
_init();
EsRandomSeed(ProcessorReadTimeStamp());
ThreadInitialise(&threadLocalStorage);
EsMessageMutexAcquire();
_init();
EsRandomSeed(ProcessorReadTimeStamp());
ThreadInitialise(&threadLocalStorage);
EsMessageMutexAcquire();
api.global = (GlobalData *) EsMemoryMapObject(api.startupInformation->globalDataRegion,
0, sizeof(GlobalData), isDesktop ? ES_MEMORY_MAP_OBJECT_READ_WRITE : ES_MEMORY_MAP_OBJECT_READ_ONLY);
theming.scale = api.global->uiScale; // We'll receive ES_MSG_UI_SCALE_CHANGED when this changes.
}
api.global = (GlobalData *) EsMemoryMapObject(api.startupInformation->globalDataRegion,
0, sizeof(GlobalData), isDesktop ? ES_MEMORY_MAP_OBJECT_READ_WRITE : ES_MEMORY_MAP_OBJECT_READ_ONLY);
theming.scale = api.global->uiScale; // We'll receive ES_MSG_UI_SCALE_CHANGED when this changes.
#ifdef PROFILE_DESKTOP_FUNCTIONS
size_t profilingBufferSize = 64 * 1024 * 1024;
GfProfilingInitialise((ProfilingEntry *) EsHeapAllocate(profilingBufferSize, true),
profilingBufferSize / sizeof(ProfilingEntry), api.startupInformation->timeStampTicksPerMs);
#endif
if (isDesktop) {
EsPrint("Reached Desktop process.\n");
#ifdef PROFILE_DESKTOP_FUNCTIONS
size_t profilingBufferSize = 64 * 1024 * 1024;
GfProfilingInitialise((ProfilingEntry *) EsHeapAllocate(profilingBufferSize, true),
profilingBufferSize / sizeof(ProfilingEntry), api.startupInformation->timeStampTicksPerMs);
#endif
// Process messages until we find the boot file system.
while (!api.foundBootFileSystem) {

View File

@ -567,14 +567,19 @@ define ES_LIST_VIEW_GROUP_COLLAPSABLE (1 << 3) // The group can be collapsed.
define ES_MENU_AT_CURSOR (1 << 0)
define ES_MENU_MAXIMUM_HEIGHT (1 << 1)
// Standard font families:
define ES_FONT_SANS (0xFFFF)
define ES_FONT_SERIF (0xFFFE)
define ES_FONT_MONOSPACED (0xFFFD)
// Font weights:
define ES_FONT_REGULAR (4)
define ES_FONT_SEMIBOLD (6)
define ES_FONT_BOLD (7)
// Font flags:
define ES_FONT_ITALIC (1 << 0)
define ES_TEXT_FIGURE_DEFAULT (0)
define ES_TEXT_FIGURE_OLD (1)
define ES_TEXT_FIGURE_TABULAR (2)
@ -1370,7 +1375,7 @@ include desktop/styles.header
struct EsFont {
EsFontFamily family;
uint8_t weight;
bool italic;
uint8_t flags;
}
struct EsTextStyle {

View File

@ -5,6 +5,8 @@
// TODO If the font size is sufficiently large disable subpixel anti-aliasing.
// TODO Variable font support.
#include <shared/bitmap_font.h>
#ifdef USE_FREETYPE_AND_HARFBUZZ
#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>
@ -24,10 +26,20 @@
#define FALLBACK_SCRIPT (0x4C61746E) // "Latn"
struct Font {
#define FONT_TYPE_BITMAP (1)
#define FONT_TYPE_FREETYPE_AND_HARFBUZZ (2)
uintptr_t type;
union {
const void *bitmapData; // All data has been validated to load the font.
#ifdef USE_FREETYPE_AND_HARFBUZZ
FT_Face ft;
hb_font_t *hb;
struct {
FT_Face ft;
hb_font_t *hb;
};
#endif
};
};
struct GlyphCacheKey {
@ -182,8 +194,6 @@ struct {
size_t bufferPosition, bufferAllocated;
} iconManagement;
Font FontGet(EsFont key);
// --------------------------------- Glyph cache.
void GlyphCacheFreeEntry() {
@ -226,141 +236,291 @@ GlyphCacheEntry *LookupGlyphCacheEntry(GlyphCacheKey key) {
// --------------------------------- Font backend abstraction layer.
bool FontLoad(Font *font, const void *data, size_t dataBytes) {
if (dataBytes > sizeof(BitmapFontHeader)) {
const BitmapFontHeader *header = (const BitmapFontHeader *) data;
if (header->signature == BITMAP_FONT_SIGNATURE && (size_t) header->headerBytes >= sizeof(BitmapFontHeader)
&& (size_t) header->headerBytes < 0x80 && (size_t) header->glyphBytes < 0x80 && (size_t) header->glyphCount < 0x8000
&& dataBytes > (size_t) header->headerBytes + (size_t) header->glyphCount * (size_t) header->glyphBytes
&& header->glyphCount >= 1 /* index 0 is used as a fallback glyph, which must be present */) {
for (uintptr_t i = 0; i < header->glyphCount; i++) {
const BitmapFontGlyph *glyph = ((const BitmapFontGlyph *) ((const uint8_t *) data + header->headerBytes + i * header->glyphBytes));
size_t bytesPerRow = (glyph->bitsWidth + 7) / 8;
size_t bitsStorage = (size_t) glyph->bitsHeight * bytesPerRow;
size_t kerningStorage = (size_t) glyph->kerningEntryCount * sizeof(BitmapFontKerningEntry);
if (glyph->bitsWidth > 0x4000 || glyph->bitsHeight > 0x4000 || glyph->kerningEntryCount > 0x4000
|| glyph->bitsOffset >= dataBytes
|| bitsStorage > dataBytes - glyph->bitsOffset
|| kerningStorage > dataBytes - glyph->bitsOffset - bitsStorage) {
return false;
}
}
font->bitmapData = data;
font->type = FONT_TYPE_BITMAP;
return true;
}
}
#ifdef USE_FREETYPE_AND_HARFBUZZ
if (!fontManagement.freetypeLibrary) {
FT_Init_FreeType(&fontManagement.freetypeLibrary);
}
if (FT_New_Memory_Face(fontManagement.freetypeLibrary, (uint8_t *) data, dataBytes, 0, &font->ft)) {
return false;
}
if (!FT_New_Memory_Face(fontManagement.freetypeLibrary, (uint8_t *) data, dataBytes, 0, &font->ft)) {
font->hb = hb_ft_font_create(font->ft, nullptr);
font->hb = hb_ft_font_create(font->ft, nullptr);
if (font->hb) {
font->type = FONT_TYPE_FREETYPE_AND_HARFBUZZ;
return true;
} else {
FT_Done_Face(font->ft);
}
}
#endif
return true;
return false;
}
void FontSetSize(Font *font, uint32_t size) {
#ifdef USE_FREETYPE_AND_HARFBUZZ
FT_Set_Char_Size(font->ft, 0, size * FREETYPE_UNIT_SCALE, 100, 100);
hb_ft_font_changed(font->hb);
#endif
}
if (font->type == FONT_TYPE_BITMAP) {
(void) size;
// Ignored.
}
uint32_t FontCodepointToGlyphIndex(Font *font, uint32_t codepoint) {
#ifdef USE_FREETYPE_AND_HARFBUZZ
return FT_Get_Char_Index(font->ft, codepoint);
#endif
}
void FontGetGlyphMetrics(Font *font, uint32_t glyphIndex, uint32_t *xAdvance, uint32_t *yAdvance, uint32_t *xOffset, uint32_t *yOffset) {
#ifdef USE_FREETYPE_AND_HARFBUZZ
FT_Load_Glyph(font->ft, glyphIndex, 0);
*xAdvance = font->ft->glyph->advance.x;
*yAdvance = font->ft->glyph->advance.y;
*xOffset = *yOffset = 0;
#endif
}
int32_t FontGetKerning(Font *font, uint32_t previous, uint32_t next) {
#ifdef USE_FREETYPE_AND_HARFBUZZ
FT_Vector kerning = {};
if (previous) FT_Get_Kerning(font->ft, previous, next, 0, &kerning);
return kerning.x;
if (font->type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
FT_Set_Char_Size(font->ft, 0, size * FREETYPE_UNIT_SCALE, 100, 100);
hb_ft_font_changed(font->hb);
}
#endif
}
int32_t FontGetAscent(Font *font) {
if (font->type == FONT_TYPE_BITMAP) {
const BitmapFontHeader *header = (const BitmapFontHeader *) font->bitmapData;
return header->yAscent * FREETYPE_UNIT_SCALE;
}
#ifdef USE_FREETYPE_AND_HARFBUZZ
return font->ft->size->metrics.ascender;
if (font->type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
return font->ft->size->metrics.ascender;
}
#endif
return 0;
}
int32_t FontGetDescent(Font *font) {
if (font->type == FONT_TYPE_BITMAP) {
const BitmapFontHeader *header = (const BitmapFontHeader *) font->bitmapData;
return header->yDescent * -FREETYPE_UNIT_SCALE;
}
#ifdef USE_FREETYPE_AND_HARFBUZZ
return font->ft->size->metrics.descender;
if (font->type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
return font->ft->size->metrics.descender;
}
#endif
return 0;
}
int32_t FontGetEmWidth(Font *font) {
if (font->type == FONT_TYPE_BITMAP) {
const BitmapFontHeader *header = (const BitmapFontHeader *) font->bitmapData;
return header->xEmWidth * FREETYPE_UNIT_SCALE;
}
#ifdef USE_FREETYPE_AND_HARFBUZZ
return font->ft->size->metrics.x_ppem;
if (font->type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
return font->ft->size->metrics.x_ppem;
}
#endif
return 0;
}
bool FontRenderGlyph(GlyphCacheKey key, GlyphCacheEntry *entry) {
#ifdef USE_FREETYPE_AND_HARFBUZZ
FT_Load_Glyph(key.font.ft, key.glyphIndex, FT_LOAD_DEFAULT);
FT_Outline_Translate(&key.font.ft->glyph->outline, key.fractionalPosition, 0);
if (key.font.type == FONT_TYPE_BITMAP) {
const BitmapFontHeader *header = (const BitmapFontHeader *) key.font.bitmapData;
const BitmapFontGlyph *glyph = ((const BitmapFontGlyph *) ((const uint8_t *) key.font.bitmapData + header->headerBytes + key.glyphIndex * header->glyphBytes));
int width;
int height;
int xoff;
int yoff;
uint8_t *output;
entry->width = glyph->bitsWidth;
entry->height = glyph->bitsHeight;
entry->xoff = -glyph->xOrigin;
entry->yoff = -glyph->yOrigin;
entry->dataBytes = sizeof(uint8_t) * 4 * glyph->bitsWidth * glyph->bitsHeight;
entry->data = (uint8_t *) EsHeapAllocate(entry->dataBytes, false);
FT_Render_Glyph(key.font.ft->glyph, FT_RENDER_MODE_LCD);
FT_Bitmap *bitmap = &key.font.ft->glyph->bitmap;
width = bitmap->width / 3;
height = bitmap->rows;
xoff = key.font.ft->glyph->bitmap_left;
yoff = -key.font.ft->glyph->bitmap_top;
entry->dataBytes = 1 /*stupid hack for whitespace*/ + width * height * 4;
output = (uint8_t *) EsHeapAllocate(entry->dataBytes, false);
if (!output) {
return false;
}
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int32_t r = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 0];
int32_t g = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 1];
int32_t b = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 2];
// Reduce how noticible the colour fringes are.
// TODO Make this adjustable?
int32_t average = (r + g + b) / 3;
r -= (r - average) / 3;
g -= (g - average) / 3;
b -= (b - average) / 3;
output[(x + y * width) * 4 + 0] = (uint8_t) r;
output[(x + y * width) * 4 + 1] = (uint8_t) g;
output[(x + y * width) * 4 + 2] = (uint8_t) b;
output[(x + y * width) * 4 + 3] = 0xFF;
// EsPrint("\tPixel %d, %d: red %X, green %X, blue %X\n", x, y, r, g, b);
if (!entry->data) {
return false;
}
size_t bytesPerRow = (glyph->bitsWidth + 7) / 8;
for (uintptr_t i = 0; i < glyph->bitsHeight; i++) {
const uint8_t *row = (const uint8_t *) key.font.bitmapData + glyph->bitsOffset + bytesPerRow * i;
for (uintptr_t j = 0; j < glyph->bitsWidth; j++) {
// TODO More efficient storage.
uint8_t byte = (row[j / 8] & (1 << (j % 8))) ? 0xFF : 0x00;
uint32_t copy = (byte << 24) | (byte << 16) | (byte << 8) | byte;
((uint32_t *) entry->data)[i * entry->width + j] = copy;
}
}
}
if (output) {
entry->data = output;
entry->width = width;
entry->height = height;
entry->xoff = xoff;
entry->yoff = yoff;
return true;
}
return false;
#ifdef USE_FREETYPE_AND_HARFBUZZ
if (key.font.type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
FT_Load_Glyph(key.font.ft, key.glyphIndex, FT_LOAD_DEFAULT);
FT_Outline_Translate(&key.font.ft->glyph->outline, key.fractionalPosition, 0);
int width;
int height;
int xoff;
int yoff;
uint8_t *output;
FT_Render_Glyph(key.font.ft->glyph, FT_RENDER_MODE_LCD);
FT_Bitmap *bitmap = &key.font.ft->glyph->bitmap;
width = bitmap->width / 3;
height = bitmap->rows;
xoff = key.font.ft->glyph->bitmap_left;
yoff = -key.font.ft->glyph->bitmap_top;
entry->dataBytes = 1 /*stupid hack for whitespace*/ + width * height * 4;
output = (uint8_t *) EsHeapAllocate(entry->dataBytes, false);
if (!output) {
return false;
}
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int32_t r = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 0];
int32_t g = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 1];
int32_t b = (int32_t) ((uint8_t *) bitmap->buffer)[x * 3 + y * bitmap->pitch + 2];
// Reduce how noticible the colour fringes are.
// TODO Make this adjustable?
int32_t average = (r + g + b) / 3;
r -= (r - average) / 3;
g -= (g - average) / 3;
b -= (b - average) / 3;
output[(x + y * width) * 4 + 0] = (uint8_t) r;
output[(x + y * width) * 4 + 1] = (uint8_t) g;
output[(x + y * width) * 4 + 2] = (uint8_t) b;
output[(x + y * width) * 4 + 3] = 0xFF;
// EsPrint("\tPixel %d, %d: red %X, green %X, blue %X\n", x, y, r, g, b);
}
}
if (output) {
entry->data = output;
entry->width = width;
entry->height = height;
entry->xoff = xoff;
entry->yoff = yoff;
return true;
}
return false;
}
#endif
return false;
}
void FontShapeTextDone(EsTextPlan *plan, uint32_t glyphCount, TextGlyphInfo *_glyphInfos, TextGlyphPosition *_glyphPositions) {
if (plan->font.type == FONT_TYPE_BITMAP) {
(void) glyphCount;
Array<TextGlyphInfo> glyphInfos = { .array = _glyphInfos };
Array<TextGlyphPosition> glyphPositions = { .array = _glyphPositions };
glyphInfos.Free();
glyphPositions.Free();
}
}
void FontShapeText(EsTextPlan *plan, const char *string, size_t stringBytes,
uintptr_t sectionOffsetBytes, size_t sectionCountBytes,
TextFeature *features, size_t featureCount,
uint32_t *glyphCount, TextGlyphInfo **glyphInfos, TextGlyphPosition **glyphPositions) {
uint32_t *glyphCount, TextGlyphInfo **_glyphInfos, TextGlyphPosition **_glyphPositions) {
if (plan->font.type == FONT_TYPE_BITMAP) {
(void) features;
(void) featureCount;
(void) stringBytes;
Array<TextGlyphInfo> glyphInfos = {};
Array<TextGlyphPosition> glyphPositions = {};
Font *font = &plan->font;
const BitmapFontHeader *header = (const BitmapFontHeader *) font->bitmapData;
const char *text = string + sectionOffsetBytes;
while (text < string + sectionOffsetBytes + sectionCountBytes) {
TextGlyphInfo info = {};
TextGlyphPosition position = {};
info.cluster = text - (string + sectionOffsetBytes);
uint32_t codepoint = utf8_value(text, string + sectionOffsetBytes + sectionCountBytes - text, nullptr);
if (!codepoint) break;
text = utf8_advance(text);
bool glyphFound = false;
uint32_t glyphIndex = 0;
ES_MACRO_SEARCH(header->glyphCount, result = codepoint - ((const BitmapFontGlyph *) ((const uint8_t *)
font->bitmapData + header->headerBytes + index * header->glyphBytes))->codepoint;, glyphIndex, glyphFound);
info.codepoint = glyphFound ? glyphIndex : 0;
const BitmapFontGlyph *glyph = ((const BitmapFontGlyph *) ((const uint8_t *)
font->bitmapData + header->headerBytes + info.codepoint * header->glyphBytes));
size_t bytesPerRow = (glyph->bitsWidth + 7) / 8;
position.x_advance = glyph->xAdvance * FREETYPE_UNIT_SCALE;
if (glyph->kerningEntryCount && text < string + sectionOffsetBytes + sectionCountBytes) {
uint32_t nextCodepoint = utf8_value(text, string + sectionOffsetBytes + sectionCountBytes - text, nullptr);
for (uintptr_t i = 0; i < glyph->kerningEntryCount; i++) {
BitmapFontKerningEntry entry;
EsMemoryCopy(&entry, (const uint8_t *) font->bitmapData + glyph->bitsOffset
+ bytesPerRow * glyph->bitsHeight + sizeof(BitmapFontKerningEntry) * i, sizeof(BitmapFontKerningEntry));
if (entry.rightCodepoint == nextCodepoint) {
position.x_advance += entry.xOffset * FREETYPE_UNIT_SCALE;
break;
}
}
}
glyphInfos.Add(info);
glyphPositions.Add(position);
}
*glyphCount = glyphInfos.Length();
*_glyphInfos = &glyphInfos[0];
*_glyphPositions = &glyphPositions[0];
return;
}
#ifdef USE_FREETYPE_AND_HARFBUZZ
hb_buffer_clear_contents(plan->buffer);
hb_buffer_set_segment_properties(plan->buffer, &plan->segmentProperties);
hb_buffer_add_utf8(plan->buffer, string, stringBytes, sectionOffsetBytes, sectionCountBytes);
hb_shape(plan->font.hb, plan->buffer, features, featureCount);
*glyphInfos = hb_buffer_get_glyph_infos(plan->buffer, glyphCount);
*glyphPositions = hb_buffer_get_glyph_positions(plan->buffer, glyphCount);
if (plan->font.type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
hb_buffer_clear_contents(plan->buffer);
hb_buffer_set_segment_properties(plan->buffer, &plan->segmentProperties);
hb_buffer_add_utf8(plan->buffer, string, stringBytes, sectionOffsetBytes, sectionCountBytes);
hb_shape(plan->font.hb, plan->buffer, features, featureCount);
*_glyphInfos = hb_buffer_get_glyph_infos(plan->buffer, glyphCount);
*_glyphPositions = hb_buffer_get_glyph_positions(plan->buffer, glyphCount);
return;
}
#endif
}
@ -377,9 +537,11 @@ uint32_t FontGetScriptFromCodepoint(uint32_t codepoint, bool *inheritingScript)
uint32_t script = hb_unicode_script(unicodeFunctions, codepoint);
*inheritingScript = script == HB_SCRIPT_COMMON || script == HB_SCRIPT_INHERITED;
return script;
#endif
#else
(void) codepoint;
*inheritingScript = false;
return FALLBACK_SCRIPT;
#endif
}
void FontInitialiseShaping(EsTextPlan *plan) {
@ -389,6 +551,8 @@ void FontInitialiseShaping(EsTextPlan *plan) {
plan->segmentProperties.direction = (plan->properties.flags & ES_TEXT_PLAN_RTL) ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
plan->segmentProperties.script = (TextScript) FALLBACK_SCRIPT;
plan->segmentProperties.language = hb_language_from_string(plan->properties.cLanguage ?: FALLBACK_SCRIPT_LANGUAGE, -1);
#else
(void) plan;
#endif
}
@ -396,6 +560,8 @@ void FontDestroyShaping(EsTextPlan *plan) {
#ifdef USE_FREETYPE_AND_HARFBUZZ
hb_buffer_destroy(plan->buffer);
plan->buffer = nullptr;
#else
(void) plan;
#endif
}
@ -681,7 +847,7 @@ Font FontGet(EsFont key) {
if (entry->files[i]) {
int weight = (i % 9) + 1;
bool italic = i >= 9;
int distance = ((italic != key.italic) ? 10 : 0) + AbsoluteInteger(weight - key.weight);
int distance = ((italic != ((key.flags & ES_FONT_ITALIC) ? true : false)) ? 10 : 0) + AbsoluteInteger(weight - key.weight);
if (distance < matchDistance) {
matchDistance = distance;
@ -691,8 +857,9 @@ Font FontGet(EsFont key) {
}
if (!file) {
EsPrint("Could not load font (f%d/w%d/i%d).\n", key.family, key.weight, key.italic);
return {};
EsPrint("Could not load font (f%d/w%d/%X).\n", key.family, key.weight, key.flags);
key.family = fontManagement.fallback;
return FontGet(key);
}
// EsPrint("Loading font from '%z' (f%d/w%d/i%d).\n", file, key.family, key.weight, key.italic);
@ -701,15 +868,17 @@ Font FontGet(EsFont key) {
void *data = EsFileStoreMap(file, &size, ES_MEMORY_MAP_OBJECT_READ_ONLY);
if (!data) {
EsPrint("Could not load font (f%d/w%d/i%d).\n", key.family, key.weight, key.italic);
return {};
EsPrint("Could not load font (f%d/w%d/%X).\n", key.family, key.weight, key.flags);
key.family = fontManagement.fallback;
return FontGet(key);
}
Font font = {};
if (!FontLoad(&font, data, size)) {
EsPrint("Could not load font (f%d/w%d/i%d).\n", key.family, key.weight, key.italic);
return {};
EsPrint("Could not load font (f%d/w%d/%X).\n", key.family, key.weight, key.flags);
key.family = fontManagement.fallback;
return FontGet(key);
}
*fontManagement.loaded.Put(&key) = font;
@ -724,9 +893,16 @@ void FontDatabaseFree() {
for (uintptr_t i = 0; i < fontManagement.loaded.Count(); i++) {
// TODO Unmap file store data.
Font font = fontManagement.loaded[i];
if (font.type == FONT_TYPE_BITMAP) {
// Nothing to be done.
}
#ifdef USE_FREETYPE_AND_HARFBUZZ
hb_font_destroy(font.hb);
FT_Done_Face(font.ft);
if (font.type == FONT_TYPE_FREETYPE_AND_HARFBUZZ) {
hb_font_destroy(font.hb);
FT_Done_Face(font.ft);
}
#endif
}
@ -1378,7 +1554,7 @@ TextStyleDifference CompareTextStyles(const EsTextStyle *style1, const EsTextSty
if (!style1) return TEXT_STYLE_NEW_FONT;
if (style1->font.family != style2->font.family) return TEXT_STYLE_NEW_FONT;
if (style1->font.weight != style2->font.weight) return TEXT_STYLE_NEW_FONT;
if (style1->font.italic != style2->font.italic) return TEXT_STYLE_NEW_FONT;
if (style1->font.flags != style2->font.flags) return TEXT_STYLE_NEW_FONT;
if (style1->size != style2->size) return TEXT_STYLE_NEW_FONT;
if (style1->baselineOffset != style2->baselineOffset) return TEXT_STYLE_NEW_SHAPE;
if (style1->tracking != style2->tracking) return TEXT_STYLE_NEW_SHAPE;
@ -1656,6 +1832,8 @@ void TextAddEllipsis(EsTextPlan *plan, int32_t maximumLineWidth, bool needFinalE
line->ellipsisPieceIndex = plan->pieces.Length();
plan->pieces.Add(piece);
}
FontShapeTextDone(plan, glyphCount, glyphInfos, glyphPositions);
}
void TextItemizeByScript(EsTextPlan *plan, const EsTextRun *runs, size_t runCount, float sizeScaleFactor) {
@ -1865,6 +2043,8 @@ int32_t TextBuildTextPieces(EsTextPlan *plan, uintptr_t sectionStart, uintptr_t
// Go to the next run.
plan->textRunPosition++;
FontShapeTextDone(plan, glyphCount, glyphInfos, glyphPositions);
}
plan->textRunPosition--;
@ -2414,7 +2594,7 @@ void EsRichTextParse(const char *inString, ptrdiff_t inStringBytes,
i++; if (i >= inStringBytes || inString[i] == ']') goto parsedFormat;
textRun->style.font.weight = inString[i] - '0';
} else if (c == 'i' /* italic */) {
textRun->style.font.italic = true;
textRun->style.font.flags |= ES_FONT_ITALIC;
} else if (c == 's' /* size */) {
textRun->style.size = 0;

View File

@ -1307,7 +1307,7 @@ const char *GetConstantString(const char *cKey) {
void ThemeStyleCopyInlineMetrics(UIStyle *style) {
style->font.family = style->metrics->fontFamily;
style->font.weight = style->metrics->fontWeight;
style->font.italic = style->metrics->isItalic;
style->font.flags = style->metrics->isItalic ? ES_FONT_ITALIC : ES_FLAGS_DEFAULT;
style->preferredWidth = style->metrics->preferredWidth;
style->preferredHeight = style->metrics->preferredHeight;
style->gapMajor = style->metrics->gapMajor;

View File

@ -777,7 +777,7 @@ static bool ControlTransferWrapper(KUSBDevice *_device, uint8_t flags, uint8_t r
void XHCIController::OnPortEnable(uintptr_t port) {
uint32_t speed = (RD_REGISTER_PORTSC(port) >> 10) & 0xF;
uint32_t maximumPacketSize = speed == SPEED_LOW ? 8 : speed == SPEED_FULL ? 8 : speed == SPEED_HIGH ? 64 : speed == 4 ? SPEED_SUPER : 0;
uint32_t maximumPacketSize = speed == SPEED_LOW ? 8 : speed == SPEED_FULL ? 8 : speed == SPEED_HIGH ? 64 : speed == SPEED_SUPER ? 512 : 0;
ports[port].speed = speed;
ports[port].controlEndpoint.maximumPacketSize = maximumPacketSize;

View File

@ -155,8 +155,6 @@ struct WindowManager {
WindowManager windowManager;
void SendMessageToWindow(Window *window, EsMessage *message);
#else
void HIDeviceUpdateIndicators() {
@ -272,9 +270,14 @@ void SendMessageToWindow(Window *window, EsMessage *message) {
window->owner->messageQueue.SendMessage(window->apiWindow, message);
}
} else if (message->type == ES_MSG_KEY_DOWN || message->type == ES_MSG_KEY_UP) {
// TODO Only send certain key messages to the container, like modifiers keys and global shortcuts.
window->embed->owner->messageQueue.SendMessage(window->embed->apiWindow, message);
window->owner->messageQueue.SendMessage(window->apiWindow, message);
if (message->keyboard.modifiers & (ES_MODIFIER_CTRL | ES_MODIFIER_ALT | ES_MODIFIER_FLAG | ES_MODIFIER_ALT_GR)) {
// Additionally send the keyboard input message to the embed parent
// if Ctrl, Alt or Flag were held.
// (Otherwise don't, to avoid increasing typing latency.)
window->owner->messageQueue.SendMessage(window->apiWindow, message);
}
} else if (message->type == ES_MSG_MOUSE_EXIT) {
window->embed->owner->messageQueue.SendMessage(window->embed->apiWindow, message);
window->owner->messageQueue.SendMessage(window->apiWindow, message);

Binary file not shown.

25
shared/bitmap_font.h Normal file
View File

@ -0,0 +1,25 @@
#define BITMAP_FONT_SIGNATURE (0xF67DD870) // (Random number.)
typedef struct BitmapFontHeader {
uint32_t signature;
uint16_t glyphCount;
uint8_t headerBytes, glyphBytes;
uint16_t yAscent, yDescent;
uint16_t xEmWidth;
uint16_t _unused0;
// Followed by glyphCount copies of BitmapFontGlyph, sorted by codepoint.
} BitmapFontHeader;
typedef struct BitmapFontKerningEntry {
uint32_t rightCodepoint;
int16_t xOffset;
uint16_t _unused0;
} BitmapFontKerningEntry;
typedef struct BitmapFontGlyph {
uint32_t bitsOffset; // Stored one row after another; each row is padded to a multiple of 8 bits.
uint32_t codepoint;
int16_t xOrigin, yOrigin, xAdvance;
uint16_t kerningEntryCount; // Stored after the bits. Not necessarily aligned!
uint16_t bitsWidth, bitsHeight;
} BitmapFontGlyph;

View File

@ -105,6 +105,11 @@ BuildFont fonts[] = {
{},
} },
{ "Bitmap Sans", "", "Sans", "Latn", (FontFile []) {
{ "4", "Bitmap Sans Regular 9.font" },
{},
} },
{},
};

View File

@ -578,7 +578,7 @@ void BuildDesktop(Application *application) {
ExecuteForApp(application, toolchainStrip, "-o", "bin/Stripped Executables/Desktop", "--strip-all", "bin/Desktop");
for (uintptr_t i = 0; i < arrlenu(fontLines); i++) {
if (fontLines[i].key[0] == '.' || 0 == strcmp(fontLines[i].key, "license")) {
if ((fontLines[i].key[0] == '.' || 0 == strcmp(fontLines[i].key, "license")) && fontLines[i].value[0]) {
snprintf(buffer, sizeof(buffer), "res/Fonts/%s", fontLines[i].value);
ADD_BUNDLE_INPUT(strdup(buffer), fontLines[i].value, 16);
}
@ -811,7 +811,7 @@ void OutputSystemConfiguration() {
"temporary=0:/" SYSTEM_FOLDER_NAME "/Temporary\n"
"default_settings=0:/" SYSTEM_FOLDER_NAME "/Settings\n"
"\n[ui_fonts]\n"
"fallback=Inter\n"
"fallback=Bitmap Sans\n"
"sans=Inter\n"
"serif=Inter\n"
"mono=Hack\n");

View File

@ -6,31 +6,12 @@
#include <stdio.h>
#include "../shared/bitmap_font.h"
#define BITS_WIDTH (50)
#define BITS_HEIGHT (50)
#define ZOOM (18)
typedef struct FileHeader {
uint16_t glyphCount;
uint8_t headerBytes, glyphHeaderBytes;
uint16_t yAscent, yDescent;
// Followed by glyphCount copies of FileGlyphHeader, sorted by codepoint.
} FileHeader;
typedef struct FileKerningEntry {
uint32_t rightCodepoint;
int16_t xOffset;
uint16_t _unused0;
} FileKerningEntry;
typedef struct FileGlyphHeader {
uint32_t bitsOffset; // Stored one row after another; each row is padded to a multiple of 8 bits.
uint32_t codepoint;
int16_t xOrigin, yOrigin, xAdvance;
uint16_t kerningEntryCount; // Stored after the bits. Not necessarily aligned!
uint16_t bitsWidth, bitsHeight;
} FileGlyphHeader;
typedef struct Kerning {
int number, xOffset;
} Kerning;
@ -49,34 +30,36 @@ UIWindow *window;
UITabPane *tabPane;
UIElement *editor;
UIElement *kerning;
UITextbox *previewText, *yAscentTextbox, *yDescentTextbox;
UITextbox *previewText, *yAscentTextbox, *yDescentTextbox, *xEmWidthTextbox;
UIScrollBar *kerningHScroll, *kerningVScroll;
Glyph *glyphsArray;
size_t glyphCount;
intptr_t selectedGlyph = -1;
int selectedPixelX, selectedPixelY;
int selectedPairX, selectedPairY, selectedPairI, selectedPairJ;
int yAscent, yDescent;
int yAscent, yDescent, xEmWidth;
char *path;
void Save(void *cp) {
FILE *f = fopen(path, "wb");
if (f) {
FileHeader header = {
BitmapFontHeader header = {
.signature = BITMAP_FONT_SIGNATURE,
.glyphCount = glyphCount,
.headerBytes = sizeof(FileHeader),
.glyphHeaderBytes = sizeof(FileGlyphHeader),
.headerBytes = sizeof(BitmapFontHeader),
.glyphBytes = sizeof(BitmapFontGlyph),
.yAscent = yAscent,
.yDescent = yDescent,
.xEmWidth = xEmWidth,
};
fwrite(&header, 1, sizeof(header), f);
uint32_t bitsOffset = sizeof(FileHeader) + glyphCount * sizeof(FileGlyphHeader);
uint32_t bitsOffset = sizeof(BitmapFontHeader) + glyphCount * sizeof(BitmapFontGlyph);
for (uintptr_t i = 0; i < glyphCount; i++) {
FileGlyphHeader glyphHeader = {
BitmapFontGlyph glyphHeader = {
.bitsOffset = bitsOffset,
.codepoint = glyphsArray[i].number,
.xOrigin = glyphsArray[i].xOrigin - glyphsArray[i].x0,
@ -88,7 +71,7 @@ void Save(void *cp) {
};
fwrite(&glyphHeader, 1, sizeof(glyphHeader), f);
bitsOffset += ((glyphHeader.bitsWidth + 7) >> 3) * glyphHeader.bitsHeight + sizeof(FileKerningEntry) * glyphHeader.kerningEntryCount;
bitsOffset += ((glyphHeader.bitsWidth + 7) >> 3) * glyphHeader.bitsHeight + sizeof(BitmapFontKerningEntry) * glyphHeader.kerningEntryCount;
}
for (uintptr_t i = 0; i < glyphCount; i++) {
@ -109,10 +92,10 @@ void Save(void *cp) {
}
for (uintptr_t i = 0; i < g->kerningCount; i++) {
FileKerningEntry entry = { 0 };
BitmapFontKerningEntry entry = { 0 };
entry.rightCodepoint = g->kerningArray[i].number;
entry.xOffset = g->kerningArray[i].xOffset;
fwrite(&entry, 1, sizeof(FileKerningEntry), f);
fwrite(&entry, 1, sizeof(BitmapFontKerningEntry), f);
}
}
@ -126,18 +109,18 @@ void Load() {
FILE *f = fopen(path, "rb");
if (f) {
FileHeader header = { 0 };
fread(&header, 1, 4, f);
BitmapFontHeader header = { 0 };
fread(&header, 1, 8, f);
if (ferror(f)) goto end;
fseek(f, 0, SEEK_SET);
fread(&header, 1, header.headerBytes > sizeof(FileHeader) ? sizeof(FileHeader) : header.headerBytes, f);
fread(&header, 1, header.headerBytes > sizeof(BitmapFontHeader) ? sizeof(BitmapFontHeader) : header.headerBytes, f);
fseek(f, header.headerBytes, SEEK_SET);
glyphCount = header.glyphCount;
glyphsArray = (Glyph *) calloc(glyphCount, sizeof(Glyph));
if (!glyphsArray) goto end;
for (uintptr_t i = 0; i < glyphCount; i++) {
FileGlyphHeader glyphHeader;
BitmapFontGlyph glyphHeader;
fread(&glyphHeader, 1, sizeof(glyphHeader), f);
if (ferror(f)) goto end;
if (glyphHeader.bitsWidth >= BITS_WIDTH || glyphHeader.bitsHeight >= BITS_HEIGHT) goto end;
@ -168,8 +151,8 @@ void Load() {
}
for (uintptr_t i = 0; i < g->kerningCount; i++) {
FileKerningEntry entry;
fread(&entry, 1, sizeof(FileKerningEntry), f);
BitmapFontKerningEntry entry;
fread(&entry, 1, sizeof(BitmapFontKerningEntry), f);
if (ferror(f)) goto end;
g->kerningArray[i].number = entry.rightCodepoint;
g->kerningArray[i].xOffset = entry.xOffset;
@ -185,6 +168,8 @@ void Load() {
UITextboxReplace(yAscentTextbox, buffer, -1, true);
snprintf(buffer, sizeof(buffer), "%d", header.yDescent);
UITextboxReplace(yDescentTextbox, buffer, -1, true);
snprintf(buffer, sizeof(buffer), "%d", header.xEmWidth);
UITextboxReplace(xEmWidthTextbox, buffer, -1, true);
end:;
@ -598,6 +583,11 @@ int main(int argc, char **argv) {
yDescentTextbox->e.cp = &yDescent;
yDescentTextbox->e.messageUser = NumberTextboxMessage;
UILabelCreate(0, 0, "The sum of the ascent and descent determine the line height.", -1);
UILabelCreate(0, 0, "X em width:", -1);
xEmWidthTextbox = UITextboxCreate(&UIPanelCreate(0, UI_PANEL_HORIZONTAL)->e, 0);
xEmWidthTextbox->e.cp = &xEmWidth;
xEmWidthTextbox->e.messageUser = NumberTextboxMessage;
UIParentPop();
UIParentPop();