From a16658eed0426477edd10402085d657ba8c1a586 Mon Sep 17 00:00:00 2001 From: nakst <> Date: Tue, 28 Dec 2021 21:24:55 +0000 Subject: [PATCH] bitmap font renderer --- apps/font_book.cpp | 2 +- apps/markdown_viewer.cpp | 2 +- desktop/api.cpp | 33 ++- desktop/os.header | 7 +- desktop/text.cpp | 392 +++++++++++++++++++-------- desktop/theme.cpp | 2 +- drivers/xhci.cpp | 2 +- kernel/windows.cpp | 11 +- res/Fonts/Bitmap Sans Regular 9.font | Bin 2872 -> 2991 bytes shared/bitmap_font.h | 25 ++ util/build.c | 5 + util/build_core.c | 4 +- util/font_editor.c | 64 ++--- 13 files changed, 378 insertions(+), 171 deletions(-) create mode 100644 shared/bitmap_font.h diff --git a/apps/font_book.cpp b/apps/font_book.cpp index 05bf41a..56bef10 100644 --- a/apps/font_book.cpp +++ b/apps/font_book.cpp @@ -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); diff --git a/apps/markdown_viewer.cpp b/apps/markdown_viewer.cpp index 0fc87d6..c9078ec 100644 --- a/apps/markdown_viewer.cpp +++ b/apps/markdown_viewer.cpp @@ -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; diff --git a/desktop/api.cpp b/desktop/api.cpp index c73af72..fa042ba 100644 --- a/desktop/api.cpp +++ b/desktop/api.cpp @@ -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) { diff --git a/desktop/os.header b/desktop/os.header index f0d4b0d..2429079 100644 --- a/desktop/os.header +++ b/desktop/os.header @@ -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 { diff --git a/desktop/text.cpp b/desktop/text.cpp index 04a1ef2..2cb2879 100644 --- a/desktop/text.cpp +++ b/desktop/text.cpp @@ -5,6 +5,8 @@ // TODO If the font size is sufficiently large disable subpixel anti-aliasing. // TODO Variable font support. +#include + #ifdef USE_FREETYPE_AND_HARFBUZZ #include #include @@ -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 glyphInfos = { .array = _glyphInfos }; + Array 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 glyphInfos = {}; + Array 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; diff --git a/desktop/theme.cpp b/desktop/theme.cpp index 1aef088..37a1995 100644 --- a/desktop/theme.cpp +++ b/desktop/theme.cpp @@ -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; diff --git a/drivers/xhci.cpp b/drivers/xhci.cpp index 45c2007..b801078 100644 --- a/drivers/xhci.cpp +++ b/drivers/xhci.cpp @@ -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; diff --git a/kernel/windows.cpp b/kernel/windows.cpp index 7fb0baa..caba62e 100644 --- a/kernel/windows.cpp +++ b/kernel/windows.cpp @@ -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); diff --git a/res/Fonts/Bitmap Sans Regular 9.font b/res/Fonts/Bitmap Sans Regular 9.font index 6d037861a2228ce9d5e270379d92e615480ec59c..9abe65c231268493464eed4a4e79a5fa9238fca5 100644 GIT binary patch literal 2991 zcmZA3Uu+ab90%~U@T8mZ?wJK8o zfW$kwIuyG;EfD&rM2A#Owupw(EPSHNt8e7*xJ%M|ig{`&5{bN4|8+Pmq*oZaL zKS}3d*TOm-Gu{Bb`3g3w!+5(J} z=Rrsluub~hn1}hY1vaVULOb4ow!${UI-LMBfO8;e2DZhC+X0)>_J98Nz;3lx&Y=Ua zX~z!3ZnIX-mt(N4PTXVXx3@30=s`Xj-Rp@YUMFr8w$HI~*n^Hu!uD%ZcC99~ z9d^LRnYF5DC+wgTw-`^D~4D2x*Cy_lL**XVXSP`e_bJ*ifdtbtiXp8JxRdf+{)S4Mb zLf^xluyswYV)_ww%$ix}G5rF2Qkz-75k~zR>?vzzydnJsd)l#oV2dkEQE(N}Gmdq^ zp0&pNu*5w!!Jb<--giTqh8@=y;hNtwirTQ}ZCu%xU9c05&BB(fnROJ>e%K36+#Kx1 z6{hG2?4@Pnc^}de>}74`J1?PEVJBgojxmAv(d)2NPTX6tSG4h4mgkyBZ^QalGn%>* zqk9K-jbrb@hO`M?mv`UyVQXOjb~sD?-ty=J*k%2q<{iVl&p(3s{kVYeeR*bm0vps8 z>fZr;_tVpSoF<_U65LFugZLmA7?|qro{D3cHiEQXZ=|x-Xe9NKkt~ekus4o-P>1(t zj-Rn5?>BtS>xN5S7hLjM;F9M*mkYM!`N7xx{JHc@M}+W%XAj{;ULa*IrRRyN5K8GY zk!I#dWr(kFmZlk>Wm%ddmYb5?j{eVon$S=P zy!dc`bjK5`LJ_XYiE_VEtN3Az8zAY;V)Nkob)s3RbR$VcfhM`tYF~>7(CVvZ1&*wW z`{JL-Z7C)_v9TyxllT&~iXt+Dj3PY(>1T1^`$3FO>LZ_?o}ZtcZMTd5!NK9-v7*V9 zilV7+hLB1(u7xex5h)K7+0sXue_adp(8nZ6q|8JnJ|^?ohBqZx$^_ryYp$3h zk(^22c0}OB^Q2VDFQ1Dq6I7Ujr!}PSpx!=sr z{PwrIStQk`$fM69qP1i;AC180b6BY`UD+=ZM+~(AaWUYskX?*DLMhW%v!aM7hzM5orYcR z*lVy&j=cff?AY6|Esnhhn|AC&*jC3rf?eU*C$MdfeF3{tTY!~v9fb51Y`fk!=21jv zU^6-{M1;xdx3C@9?{)*5XG!0~u5#jjgw1OEKYzc%uC|8DuWRTJ*qmekz^<`Y<=Kyk zb~mAz!>sT|jrf56t2FG^7_E@Xtya#rpV|!uq z*04-uSw`1SH_G?r2UQOsb*gZDR+^dRyg5B%H z{SG@|&EzGZKVkQ2;~aCX3Hl3mzm2P&2j^i2wFS1uL;4r?fQ>Wb#S~$Yi%wh*>_I24 zA9l!z8-g9SaT3|{k*#^yL#yHxEx;aj#ybFeL|bIz=q=dej=c+e!Wut^CC>3R?D&fD zb2p^3uqU-e_C14l3hb#>}kh-gFR!-+(#jugPm~V&cjZwGDNQ-dUnOQ?n6pp zr?ge?yo3f|&%wG~V*>A^HtcyPZW8u_w#e3+M;l>7YZ*;niK{*ZyV$YKurX~y_vNSW zG;AHL+vRHE_ZF_6&>IPUS$Dg*34>r{WVX?mjboWMgS4-&naXyvne#rtGFyYO-YOPSzT{LCGZ z8st_KzMY7`j^|0KlwVy9Unb}<3r{nQ+d?GIBClf(BF8LP;b~sr5J`>nSXDh#*L}%e zZ+-||c>PW1EUkvk@n59g@ZXi00w0-lnIG`IwZmIaFQ1sNW}ccU31NrJdi08_Nw~$x zPe7goKLxl=gca)9v=Etoenmx@v6Hi78#VGCj_r$L@)-!31|! Jk~EvX{~uBp$cg{} diff --git a/shared/bitmap_font.h b/shared/bitmap_font.h new file mode 100644 index 0000000..4b14807 --- /dev/null +++ b/shared/bitmap_font.h @@ -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; diff --git a/util/build.c b/util/build.c index 3770ff8..cd0ff1a 100644 --- a/util/build.c +++ b/util/build.c @@ -105,6 +105,11 @@ BuildFont fonts[] = { {}, } }, + { "Bitmap Sans", "", "Sans", "Latn", (FontFile []) { + { "4", "Bitmap Sans Regular 9.font" }, + {}, + } }, + {}, }; diff --git a/util/build_core.c b/util/build_core.c index 1997048..ee1ef92 100644 --- a/util/build_core.c +++ b/util/build_core.c @@ -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"); diff --git a/util/font_editor.c b/util/font_editor.c index 12c2c70..a28b1f9 100644 --- a/util/font_editor.c +++ b/util/font_editor.c @@ -6,31 +6,12 @@ #include +#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();